Happens that i need to track file status through ssh by php(using phpunit). But when i trying to launch this code:
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$cmd = "ssh hostname 'tail -F ~/test.file'";
$proc = proc_open($cmd, $descriptorspec, $pipes, null);
$str = fgets($pipes[1]);
echo $str;
if (!fclose($pipes[0])) {
throw new Exception("Can't close pipe 0");
}
if (!fclose($pipes[1])) {
throw new Exception("pipe 1");
}
if (!fclose($pipes[2])) {
throw new Exception("pipe 2");
}
$res = proc_close($proc);
nothing happens - no output, and i guess deadlock exucuted: script doesn't exit.
Have any ideas? or suggestions?
tail -F doesn't actually "end" - it just keeps dumping output as it becomes available. That's probably the problem. It's blocking on the fgets().
My recommendation: use phpseclib, a pure PHP SSH2 implementation. eg.
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
function packet_handler($str)
{
echo $str;
}
$ssh->exec('tail -F ~/test.file', 'packet_handler');
?>
Although looking at the implementation, now... it doesn't look like it provides you with any mechanism to prematurely quit either. It'd be nice if it was like "if packet_handler returns false then exec() stops running" or something.
I guess in lieu of that you can use ->setTimeout().
Related
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.
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.
I have written a simple chat program using twisted library in Python. Basically I have a server program(server.py) and a chat program ( client.py)
client.py is a simple python script which would connect to the server on a particular port and print the messages on the terminal.
I can run the server.py and the client.py on a local system and I can chat on different terminals.
I would like to integrate the client.py in PHP and be able to chat through the browser.
I am calling python script through exec in PHP. However it is not working.
exec("python client.py")
Any idea, if I am missing anything ?
Not sure if I can help you, but here's some things I'd consider:
Have you tried executing a different command, does that work?
Have you set the permissions of the .php file and Python.exe correctly?
Can you run your command from a terminal window (either Windows/Mac/Linux) from the folder your PHP file is in?
If you've already tried all of these things, I can't think of another solution.. Good luck!
You might find the following program helpful as a starting point. It is designed to run a Python program:
<?php
// Check that test is not FALSE; otherwise, show user an error.
function assert_($test)
{
if ($test === FALSE)
{
echo '<html><head><title>Proxy</title></head><body><h1>Fatal Error</h1></body></html>';
exit(1);
}
}
// Patch this version of PHP with curl_setopt_array as needed.
if (!function_exists('curl_setopt_array')) {
function curl_setopt_array($ch, $curl_options)
{
foreach ($curl_options as $option => $value) {
if (!curl_setopt($ch, $option, $value)) {
return FALSE;
}
}
return TRUE;
}
}
// Fetch the URL by logging into proxy with credentials.
function fetch($url, $proxy, $port, $user, $pwd)
{
$ch = curl_init($url);
assert_($ch);
$options = array(
CURLOPT_PROXY => $proxy . ':' . $port,
CURLOPT_PROXYAUTH => CURLAUTH_NTLM,
CURLOPT_PROXYUSERPWD => $user . ':' . $pwd,
CURLOPT_RETURNTRANSFER => TRUE
);
assert_(curl_setopt_array($ch, $options));
$transfer = curl_exec($ch);
curl_close($ch);
assert_($transfer);
return $transfer;
}
// Run path with stdin and return program's status code.
function order($path, $stdin, &$stdout, &$stderr)
{
$cmd = './' . basename($path);
$descriptorspec = array(
array('pipe', 'r'),
array('pipe', 'w'),
array('pipe', 'w')
);
$cwd = dirname($path);
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $_REQUEST);
assert_($process);
for ($total = 0; $total < strlen($stdin); $total += $written)
{
$written = fwrite($pipes[0], substr($stdin, $total));
assert_($written);
}
assert_(fclose($pipes[0]));
$stdout = stream_get_contents($pipes[1]);
assert_($stdout);
assert_(fclose($pipes[1]));
$stderr = stream_get_contents($pipes[2]);
assert_($stderr);
assert_(fclose($pipes[2]));
return proc_close($process);
}
// Collect information to run over the proxy.
# $user = $_REQUEST['user'];
# $pwd = $_REQUEST['pwd'];
// Fetch the URL and process it with the backend.
$transfer = fetch('http://rssblog.whatisrss.com/feed/', 'labproxy.pcci.edu', 8080, $user, $pwd);
$status = order('/home/Chappell_Stephen/public_html/backend.py', $transfer, $stdout, $stderr);
// Check for errors and display the final output.
assert_(strlen($stderr) == 0);
echo $stdout;
?>
I'm trying to do GPG encryption on a Windows platform, in PHP, running XAMPP.
The webserver is Apache and is running PHP 5.2.9.
I'm using GPG4Win 2.0.4.
I've had success running the encrypt command from the command line. I've changed the recipient and host names.
C:\>C:\PROGRA~1\GNU\GnuPG\pub\gpg.exe --encrypt --homedir C:\DOCUME~1\reubenh.AD\APPLIC~1\gnupg --recipient name#host.com --armor < test.txt > test.enc.txt
In PHP, I'm using proc_open() so I can pipe the content to be encrypted directly to the process, and use the stdout pipe to grab the output.
Following is a snippet of the code:
$cmd = Configure::read('Legacy.GPG.gpg_bin').' --encrypt '.
'--homedir '.Configure::read('Legacy.GPG.gpg_home').' '.
'--recipient '.Configure::read('Legacy.MO.gnugp_keyname').' '.
'--local-user '.'me#host.com'.' '.
'--armor --no-tty --batch --debug-all';
error_log('Encrypting Command line is '.$cmd);
$descriptors = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('file', LOGS.'gpg.log', 'a')
);
$process = proc_open($cmd, $descriptors, $pipes);
if (is_resource($process)) {
error_log(print_r($pipes, true));
list($stdin, $stdout) = $pipes;
error_log('Have pipes');
error_log('body length is '.strlen($this->request['body']));
$ret = fwrite($stdin, $this->request['body'], strlen($this->request['body']));
error_log($ret.' written');
error_log('before fclose()');
fclose($stdin);
error_log('Done with input');
$encryptedData = '';
while(!feof($stdout)) {
$line = fgets($stdout);
error_log('Line read:'.error_log(print_r($line, true)));
$encryptedData .= $line;
}
fclose($stdout);
$return = proc_close($process);
error_log('Encryption process returned '.print_r($return, true));
if ($return == '0') { // ... next step is to sign
The generated command from the first error_log() statement is:
C:\PROGRA~1\GNU\GnuPG\pub\gpg.exe --encrypt --homedir C:\DOCUME~1\reubenh.AD\APPLIC~1\gnupg --recipient name#host.com --local-user me#host.com --armor --no-tty --batch --debug-all
The actual running seems to get as far as "Have pipes". After that, it just stops.
I can also see in the Process Explorer, that the gpg.exe also spawns a gpg2.exe. I suspect that it is this gpg2.exe that I do not have a handle to, is waiting for the input, not the original gpg.exe that I invoked.
I've tried invoking gpg2.exe directly, but a child gpg2.exe is still spawned.
I'd rather use proc_open(), and avoid using disk I/O to provide the content and grab the output, since this will be run on a webserver, and proc_open() will allow me to not bother generating unique files, and then having to clean them up.
I've ended up compromising, to get the solution initially "work", although I'm not very happy about the way it's been done.
The problem seemed to be in two parts.
The first part was the process would hang when trying to sign, and use the --passwd-fd option. If I left this option out, I would get a prompt through the interactive nature of the webserver, enter it manually, and everything would be ok. The workaround, for an unattended application, is to simply have no passphrase. I've seen recommendations in various GnuPG forums to the effect that if your passphrase is going to stored as plain text on the same machine as the private key, then you may as well dispense with the pretence and don't have one. No passphrase is working for the moment.
The second part was that the input was too large. The magic number seemed to be 72kb. Any payload to be encrypted larger than that, using proc_open and a standard pipe just didn't seem to work. As a result, I've opted for temporarily writing the payload to a file, to be read by the proc_open. See as follows:
$tmpfile = tmpfile();
fwrite($tmpfile, $this->request['body']);
fseek($tmpfile, 0);
$cmd = '...'; // similar to question command, but with --sign --encrypt and no --passphrase-fd
$descriptors = array(
0 => $tmpfile,
1 => array('pipe', 'w'),
2 => array('file', LOGS.'gpg.log', 'a')
);
$options = array('bypass_shell' => true);
$process = proc_open($cmd, $descriptors, $pipes, null, null, $options);
if (is_resource($process)) {
stream_set_blocking($pipes[1], 0);
fclose($tmpfile);
$encryptedData = '';
$line = fgets($pipes[1]);
while (!feof($pipes[1])) {
$encryptedData .= $line;
$line =fgets($pipes[1]);
}
fclose($pipes[1]);
$return = proc_close($process);
if ($return = '0') {
// success processing
}
}
I elected not to use list() = $pipes, because only the stdout pipe would actually return in the array.
If anyone had has experience with GPG and PHP in a Windows environment, I'd be more than welcome to hear something, even if it's years down the track.
I'm trying to write a helper script for doing various admin tasks on a server which can only be done from the command line and trying to use the 'dialog' command to display message boxes, inputs, password prompts etc, however, the needs of this task call for me to process the data in PHP.
I'm having problems getting the dialog command to work in this way and can't figure out what i'm doing wrong.
There's an example here
Unfortunately it doesn't work.
When you run PHP and exec/backtick/system to an external application, the IO doesn't appear to work how you'd expect.
The nearest I can get is using the passthru() command:
<?php
$CMD = "dialog --menu \"Please select\" 10 40 3 backup \"Backup Files\" restore \"Restore Files\"";
passthru($CMD);
?>
This is the only way that PHP will let dialog use the STDOUT properly, anything else results in no display but you can press return to select an option.
I've tried backticks, exec() and system() but nothing seems to work.
What I wondered was how to read STDERR properly from within PHP to get the return value into a variable called $result.
I'm sure some other sysadmins have had to do this before.
My reasons for not using bash for this are that one command I have to execute as a result of a selection produces XML output only and I can't parse that effectively in bash.
Just in case someone else is searching for this:
function dialog ($args) {
$pipes = array (NULL, NULL, NULL);
// Allow user to interact with dialog
$in = fopen ('php://stdin', 'r');
$out = fopen ('php://stdout', 'w');
// But tell PHP to redirect stderr so we can read it
$p = proc_open ('dialog '.$args, array (
0 => $in,
1 => $out,
2 => array ('pipe', 'w')
), $pipes);
// Wait for and read result
$result = stream_get_contents ($pipes[2]);
// Close all handles
fclose ($pipes[2]);
fclose ($out);
fclose ($in);
proc_close ($p);
// Return result
return $result;
}
It requires dialog (apt-get install dialog) and proc_xxx (PHP 4.3.0, PHP 5)
It works, at least for me. :)
You can use proc_open() but not as show above... All dialog boxes do not work in same way. I provides a concrete sample below:
#!/usr/bin/env php
<?php
$pipes = array();
$process = null;
$output = '';
$ret = -1;
/**
* Start process
*
* #param string $cmd Command to execute
* #param bool $wantinputfd Whether or not input fd (pipe) is required
* #retun void
*/
function processStart($cmd, $wantinputfd = false)
{
global $process, $pipes;
$process = proc_open(
$cmd,
array(
0 => ($wantinputfd) ? array('pipe', 'r') : STDIN, // pipe/fd from which child will read
1 => STDOUT,
2 => array('pipe', 'w'), // pipe to which child will write any errors
3 => array('pipe', 'w') // pipe to which child will write any output
),
$pipes
);
}
/**
* Stop process
*
* #return void
*/
function processStop()
{
global $output, $pipes, $process, $ret;
if (isset($pipes[0]) {
fclose($pipes[0]);
usleep(2000);
}
$output = '';
while ($_ = fgets($pipes[3])) {
$output .= $_;
}
$errors = '';
while ($_ = fgets($pipes[2])) {
fwrite(STDERR, $_);
$errors++;
}
if ($errors) {
fwrite(STDERR, "dialog output the above errors, giving up!\n");
exit(1);
}
fclose($pipes[2]);
fclose($pipes[3]);
do {
usleep(2000);
$status = proc_get_status($process);
} while ($status['running']);
proc_close($process);
$ret = $status['exitcode'];
}
// Test for yesno dialog box
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --yesno 'yesno dialog box' 0 70");
processStop();
echo "Exit code is $ret\n";
// Test for gauge dialog box
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --gauge 'Gauge dialog box' 0 70 0", true);
sleep(1);
fwrite($pipes[0], "XXX\n0\nFirst step\nXXX\n20\n");
sleep(1);
fwrite($pipes[0], "XXX\n20\nSecond step\nXXX\n50\n");
sleep(1);
fwrite($pipes[0], "XXX\n50\nThird step\nXXX\n80\n");
sleep(1);
fwrite($pipes[0], "XXX\n80\nFourth step\nXXX\n100\n");
sleep(1);
processStop();
echo "Exit code is $ret\n";
// Test for input dialog box
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --inputbox 'input dialog box' 0 70");
processStop();
echo "Output is $output\n";
echo "Exit code is $ret\n";
// Test for errors output
processStart("dialog --backtitle 'dialog test' --title 'Little test' --output-fd 3 --dummy 'my input box' 0 70");
processStop();
exit(0);
You can use proc_open() to run a command and interact on all pipes, STDIN, STDOUT and STDERR:
$pipes = array(NULL, NULL, NULL);
$proc = proc_open(
"dialog --gauge ..",
array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
),
$pipes
);
print fgets($pipes[2]);
See the manual for more examples.
PHP-GTK looks like a solution for this problem
http://gtk.php.net/
I think you can't run ncurses application through PHP like this - maybe you should take a look at this: http://php.net/manual/de/book.ncurses.php