Pipe $_POST to an external command - php

I have a program that reads a JSON request from stdin, which I want to call in PHP.
Here's what I have right now
<?php
echo exec(
'echo \''.json_encode($_POST,JSON_NUMERIC_CHECK).'\' | '.
'program'
);
?>
This works, but is there a more direct way to put a string in PHP in stdin?
Something along the lines of pipe(json_encode($_POST,JSON_NUMERIC_CHECK),'program') maybe?
What I have may be fine in this particular case, but what if instead of JSON, I'll need to pipe binary data? What if it's too long to fit into a shell argument, or contains single quotes?
Edit:
Following miken32's suggestion, I used proc_open() like this:
$proc = proc_open(
'LD_LIBRARY_PATH=/foo/bar/lib program args',
array(0 => array('pipe','r'), 1 => array('pipe','w')),
$pipes,
NULL
//, array('LD_LIBRARY_PATH','/foo/bar/lib')
);
if (is_resource($proc)) {
fwrite($pipes[0],json_encode($_POST,JSON_NUMERIC_CHECK));
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($proc);
}
?>
But I had to add LD_LIBRARY_PATH=/foo/bar/lib to the command argument. The $env argument seems to have no effect. Does anyone know why?

You can use popen() to create a pipeline to a process:
<?php
$data = json_encode($_POST, JSON_NUMERIC_CHECK);
$p = popen("program", "w");
fwrite($p, $data . PHP_EOL);
$exit_code = pclose($p);
If you need to get data back from the program, things get more involved and you need to use proc_open() instead.
<?php
$data = json_encode($_POST, JSON_NUMERIC_CHECK);
$fds = [
0=>["pipe", "r"], // STDIN
1=>["pipe", "w"], // STDOUT
2=>["pipe", "w"], // STDERR
];
$dir = "/path/to/working/directory";
$env = [
"PATH" => "/usr/local/foo/bin:/usr/local/bin:/usr/bin",
"LD_LIBRARY_PATH" => "/usr/local/foo/lib/",
];
$p = proc_open("program", $fds, $pipes, $dir, $env);
fwrite($pipes[0], $data . PHP_EOL);
fclose($pipes[0]);
$return = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$exit_code = proc_close($p);
if ($exit_code == 0) {
// successful return
echo $return;
} else {
// error
echo "ERR: $err";
}

Related

run command line program and enter password from php

I have a command line program which requires for the user to type a password in order to run. I want to run it and enter the password through a php script.
I have tried using proc_open, using the following code, but it does not work. It reads the input, but fwrite does not seem to do anything.
$cmd = "cmd /c command_line_program";
$descriptorspec = array(
0 => array('pipe', 'r'), //STDIN
1 => array('pipe', 'w'), //STDOUT
2 => array('pipe', 'r'), //STDERR
);
$process = proc_open(
$cmd,
$descriptorspec, $pipes, null, null);
if (is_resource($process)) {
$buffer = "";
// Read from the command's STDOUT until it's closed.
while (!feof($pipes[1])) {
$input = fread($pipes[1], 8192);
$buffer .= $input;
if (strpos($buffer, 'Password:') !== false){
fwrite($pipes[0], "userpass\n");
}
}
proc_close($process);
} else {
echo 'Not a resource';
}
What am I doing wrong? Is there some other solution except proc_open? I am using windows and php 7.2.
According to PHP proc-open.php documentation and the comments bellow are recommendation to:
close all three streams (store the return value issue):
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
Assign the return value to a variable ( passing passwords to the process)
$exit_status = proc_close($process);
Both are missing in your code.

(PHP) Live output proc_open

i have tried many time by using flush() to make the script work synchronously, the script prints only data of the first command "gcloud compute ssh yellow" and "ls -la", I am looking to make the script prints the output on every executed fputs().
<?php
$descr = array( 0 => array('pipe','r',),1 => array('pipe','w',),2 => array('pipe','w',),);
$pipes = array();
$process = proc_open("gcloud compute ssh yellow", $descr, $pipes);
if (is_resource($process)) {
sleep(2);
$commands = ["ls -la", "cd /home", "ls", "sudo ifconfig", "ls -l"];
foreach ($commands as $command) {
fputs($pipes[0], $command . " \n");
while ($f = fgets($pipes[1])) {
echo $f;
}
}
fclose($pipes[0]);
fclose($pipes[1]);
while ($f = fgets($pipes[2])) {
echo "\n\n## ==>> ";
echo $f;
}
fclose($pipes[2]);
proc_close($process);
}
Thanks in advance
I believe the problem is the loop you have waiting for input. fgets will only return false if it encounters EOF. Otherwise it returns the line that it read; because the linefeed is included, it doesn't return anything that can be typecast to false. You can use stream_get_line() instead, which does not return the EOL character. Note this would still require your command to return an empty line after its output so it can evaluate to false and break the while loop.
<?php
$prog = "gcloud compute ssh yellow";
$commands = ["ls -la", "cd /home", "ls", "sudo ifconfig", "ls -l"];
$descr = [0 => ['pipe','r'], 1 => ['pipe','w'], 2 =>['pipe','w']];
$pipes = [];
$process = proc_open($prog, $descr, $pipes);
if (is_resource($process)) {
sleep(2);
foreach ($commands as $command) {
fputs($pipes[0], $command . PHP_EOL);
while ($f = stream_get_line($pipes[1], 256)) {
echo $f . PHP_EOL;
}
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
}
Another option would be to gather the output outside the loop, although this would require you to parse the output if you need to know what output came from what command.
<?php
$prog = "gcloud compute ssh yellow";
$commands = ["ls -la", "cd /home", "ls", "sudo ifconfig", "ls -l"];
$descr = [0 => ['pipe','r'], 1 => ['pipe','w'], 2 =>['pipe','w']];
$pipes = [];
$process = proc_open($prog, $descr, $pipes);
if (is_resource($process)) {
sleep(2);
foreach ($commands as $command) {
fputs($pipes[0], $command . PHP_EOL);
}
fclose($pipes[0]);
$return = stream_get_contents($pipes[1]);
$errors = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
}

Working with PHP STDIN and proc_open and Node JS command

I'm trying to utilize a node js package called can-compile which reads the contents from an ejs file and then converts the data to a usable and canjs friendly output. I'm trying to avoid saving the template data to a file on the server and using said file to convert the template data. That is where I have been trying to use php's STDIN/OUT.
The compiler takes the name of the template file as a parameter to read from. I've tried various methods of passing the template data to node command line with no success.
Ultimately what I'm trying to achieve is being able to send the uncompiled template data to the STDIN/OUT pipe and have it return the compiled code from the can-compile node package.
Can someone please point me in the right direction of what I should be doing. Here I'm using a small template example (see $input). But the template sizes vary up to hundreds of lines and characters.
$template_name = 'template_'.$template_data['name'].'.ejs';
$can_compiler = "/node_modules/can-compile/bin/can-compile --can 1.1.5 $template_name";
$input = "<img src="/media/<%==category.attrs.image%>" style="width:100%; height:100%;" />";
$cmd = sprintf("node %s",$can_compiler);
$descriptorspec = array(
0 => array('pipe','r'),
1 => array('pipe','w'),
2 => array('pipe','w')
);
$process = proc_open($cmd, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $input);
fclose($pipes[0]);
$template_content = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$error_content = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$return_value = proc_close($process);
return $template_content;
}
I've already search through stack-overflow and found this How to pass variables as stdin into command line from PHP. The strange issue I'm running into is that my code has worked yesterday but not today. Maybe a fresh set of eyes can help me out.
I have figured out the issue, I was missing the file_put_contents() function when sending data to the pipe...
Here is the working code...
$template_name = 'template_test.ejs';
$input = '<img src="/media/<%==category.attrs.image%>" style="width:100%; height:100%;" />';
$cmd = "node /node_modules/can-compile/bin/can-compile --can 1.1.5 $template_name";
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w")
);
$process = proc_open($cmd, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], file_put_contents($template_name,$input));
fclose($pipes[0]);
$template_name = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$return_value = proc_close($process);
echo $template_name;
}

How to run python script and compare the output inside php

I have a php script that runs a python script. I have to compare python script's output with specific constants in php script.
I tried using exec and popen. Here is the code I tried so far
$out=NULL;
$pid=exec("python /home/krishna/online/createProblems.py $contest $pcode $fn1 $fn2",$out) or die("error");
if ($out=="1"){echo "Successfully inserted problem";}
and using popen
$pid=popen("python /home/krishna/online/createProblems.py $contest $pcode $fn1 $fn2","r") or die("error");
$ot=fread($pid,256);
if ($ot=="1"){echo "Successfully inserted problem";}
Both codes are not working properly....
When I tested outputs I got "1" as output. but comparing with "1" is not working.
You can use pipes for stdin, stdout and stderr.
function get_output($cmd) {
$descriptorspec = array(0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w')); // stderr
$process = proc_open($cmd, $descriptorspec, $pipes);
$output = '';
if (is_resource($process)) {
fwrite($pipes[0], 'some std input can be here'); // not necessary
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if (!empty($err)) {
throw new Exception();
}
}
return $output;
}
Now you should pass the needed $cmd to be executed.

How to make fread blocking and read until the end?

I am executing a TCL script from PHP using proc_open.
I first open the TCL shell
2) Send a command using fwrite
3) What I need is fread to wait/block until the
command sent by fwrite is complete
and get all the contents .The command may take some time to complete.
(I am able to read just 2 lines and then it is going off to the next loop)
Can someone guide me.
The present code is
<?php
$app = 'tclsh84';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/wamp/www/tcl/bin/g.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process))
{
for($i=0;$i<4;$i++)
{
fwrite($pipes[0], 'source c:/wamp/www/tcl/bin/test.tcl'."\n");
$content= fread($pipes[1],8192)
print "$content";
}
fclose($pipes[0]);
fclose($pipes[1]);
proc_close($process);
}
?>
I'm thinking about a combination of
stream_select and/or feof()
fread() and concatenation of the partial results ($result .= fread())
and maybe proc_get_status() to determine the end of the process
You want to wait until the tcl application doesn't write something to its stdout for a certain amount of time (presuming that this means the end of the last command) and then send the next command/line to its stdin?
edit:
Seems like you can send all commands to the tcl shell at once and they are processed one by one, i.e. the shell reads the next input line/command when it's done with the previous one. I've tested this with the script.
incr a 1
after 1000
puts [concat [clock seconds] $a]
and
<?php
$app = 'c:/programme/tcl/bin/tclsh85.exe';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/god.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "set a 1\n");
for($i=0;$i<4;$i++) {
fwrite($pipes[0], "source c:/helloworld.tcl\n");
}
// when all scripts are done the shell shall exit
fwrite($pipes[0], "exit\n");
fclose($pipes[0]);
do {
$read=array($pipes[1]); $write=array(); $except=array($pipes[1]);
// wait up to 1 second for new output of the tcl process
$ready = stream_select($read, $write, $except, 1, 0);
if ( $ready && $read /* is not empty */) {
// get the partial output
$r = fread($pipes[1], 2048);
echo $r;
}
// is the process still running?
$status = proc_get_status($process);
} while($status['running']);
fclose($pipes[1]);
proc_close($process);
}
?>
You probably want to add some more error handling. E.g. if stream_select() returns x times with an timeout something might have gone wrong.
edit2:
Let the shell print something you can scan for after each script.
<?php
// something that's not in the "normal" output of the scripts
$id = 'done'. time();
$app = 'c:/programme/tcl/bin/tclsh85.exe';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/god.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "set a 1\n");
for($i=0;$i<4;$i++) {
$output = '';
$continue = true;
$cTimeout = 0;
echo 'loop ', $i, "\n";
fwrite($pipes[0], "source c:/helloworld.tcl\n");
fwrite($pipes[0], "puts $id\n");
echo "waiting for idle\n";
do {
$read=array($pipes[1]);
$write=array();
$except=array($pipes[1]);
$ready = stream_select($read, $write, $except, 1, 0);
if ( $ready && $read ) {
$output .= fread($pipes[1], 2048);
// if the delimiter id shows up in $output
if ( false!==strpos($output, $id) ) {
// the script is done
$continue = false;
}
}
} while($continue);
echo 'loop ', $i, " finished\n";
}
proc_close($process);
}
?>
Try:
$content = '';
while(!feof($pipes[1]))
{
$content .= fread($pipes[1],8192);
}
Does that wait?

Categories