Using linux 'dialog' command from PHP - php

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

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 - `shell_exec` not working with NMap (Windows Server)

I've been trying to figure out why I can't get NMap to give me any sort of output nor even work for that matter via PHP.
Things I've tried so far:
// this doesn't return anything because it's wrong
$output = passthru('nmap -V');
echo $output;
// this returns a negated integer value
passthru('nmap -V', $output);
echo $output;
// this doesn't return anything either
$stream = popen('C:\nmap -V', 'r');
while (!feof($stream))
{
$buffer = fread($stream, 1024);
echo $buffer;
}
pclose($stream);
// this doesn't do anything as well
$output = system('C:\nmap -V');
echo $output;
// this does nothing also...
ob_start(); // start output buffering
fpassthru('C:\nmap -V'); // flush COMPLETE output of nmap
$output = ob_get_contents(); // capture output buffer contents
ob_end_clean(); // shutdown output buffers
echo $output; // echo it
.
// okay, how about we try a 'proc_open()'?
// nope, this doesn't work either. I just get a value of "command returned -1073741515"
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "errors/errors.txt", "a") // stderr is a file to write to
);
$cwd = 'errors';
$env = array('some_option' => 'aeiou');
$process = proc_open('C:/nmap -V', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process))
{
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /errors/errors.txt
fwrite($pipes[0], '<?php print_r($_ENV); ?>');
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
And many others, but I get absolutely NOTHING back from $output. I've done a lot of Google searching too, but I still can't figure it out. Many examples also seem to be for Linux which doesn't help.
Thanks.
Okay, I get an output using this code. I will continue coding and finish the rest of the program. Thanks to 'Chris Haas' for the suggestion in using proc_open
NOTE: The directory that contains the 'errors.txt' file must have 'IIS_IUSRS' write permissions. When in doubt, check your PHP error log.
$descriptorSpec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "errors/errors.txt", "a") // stderr is a file to write to
);
$env = array('bypass_shell' => true);
$process = proc_open("NMAP.EXE -V", $descriptorSpec, $pipes, "C:\\Program Files (x86)\\NMap", $env);
if (is_resource($process))
{
// '$pipes' now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
fwrite($pipes[0], '<?php print_r($_ENV); ?>');
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
// it is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
echo "<br /><br />Command Returned: $return_value\n";
}
Nmap version 7.91 ( https://nmap.org ) Platform:
i686-pc-windows-windows Compiled with: nmap-liblua-5.3.5
openssl-1.1.1h nmap-libssh2-1.9.0 nmap-libz-1.2.11 nmap-libpcre-7.6
Npcap-1.00 nmap-libdnet-1.12 ipv6 Compiled without: Available nsock
engines: iocp poll select
Command Returned: 0

Run .exe in web app and real-time communication with this .exe

My PHP web app receives data from a stream. Once the page is loaded I need to open an .exe file using system() or exec() and after short period of time new data will come, so I must type specific command to this .exe to get its returned value, how can I do this?
I'm only able to do this manually in command prompt
path/to/.exe :: hit 'Enter'
command1 params1
//...
What you're looking for is proc_open(). http://php.net/manual/en/function.proc-open.php
This will allow you to work with STDIO streams to communicate with the separate process.
Example from the PHP documentation:
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);
$cwd = '/tmp';
$env = array('some_option' => 'aeiou');
$process = proc_open('php', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt
fwrite($pipes[0], '<?php print_r($_ENV); ?>');
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
You could also consider shared memory if you need more than one listener, but this scenario sounds like you would benefit from using a queue.
Documentation msg_get_queue, msg_receive, msg_send
Example
// Send
if (msg_queue_exists(12345)) {
$mqh = msg_get_queue(12345);
$result = msg_send($mqh , 1, 'data', true);
}
// Receive
$mqh = msg_get_queue(12345, 0666);
$mqst = msg_stat_queue($mqh);
while ($mqst['msg_qnum']) {
msg_receive($mqh, 0, $msgtype, 2048, $data, true);
// Spawn your process
$mqst = msg_stat_queue($mqh);
}
Edit
Semaphore functions aren't available on Windows, as suggested above your best bet is to go with popen (unidirectional) or proc_open for bi-directional support.

Proc_open() c++/python

I'm trying to make a call for a c++ / python file with proc_open (bi-directional support needed).
After doing some on-line research I found created this code: (I first tried it with c++, after failure I tried python as well)
PHP:
<?php
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "error-output.txt", "a")
);
$process = proc_open('new.exe', $descriptorspec, $pipes);
$input = 20;
$exp = 2;
if (is_resource($process)) {
print fgets($pipes[1]);
fwrite($pipes[0], $input);
print fgets($pipes[1]);
fwrite($pipes[0], $exp);
print fgets($pipes[1]);
fclose($pipes[1]);
fclose($pipes[0]);
$return_value = proc_close($process);
echo "command returned $return_value\n";
} else {
echo "No resource availeble";
}
?>
C++:
#include <iostream>
using namespace std;
int main () {
// Get variables from php
cout << "input" << endl;
cin << input
cout << "exponent" << endl;
cin << exp
// Process variables
int answer = input + exp;
// Return variables
cout << answer;
// End c++ script
return 0;
}
Python:
print 'enter input'
inp = raw_input()
print 'enter exponent'
exp = raw_input()
ant = inp + exp
print ant
But sadly enough it kept failing with the same error: file is not recognized as internal or external command, program or batch file.
Some extra information:
I used Wamp with PHP 5.3.0
The return value I get from proc_close() = 1
There are two problems in you code (at least for running your Python script). First, you're not flushing your Python output to it's STDOUT, so it never reaches PHP. This causes fgetc() to block infinitely. It's a simple fix, just add some flush() calls:
#!/usr/bin/env python
import sys
print 'enter input'
sys.stdout.flush()
inp = raw_input()
print 'enter exponent'
sys.stdout.flush()
exp = raw_input()
ant = inp + exp
print ant
sys.stdout.flush()
Then, in your PHP code you are not sending any newlines when you write to the Python script STDIN. So, Python's raw_input() waits indefinitely for a newline. Again, an easy fix. Just add "\n" to the fwrite() calls:
#!/usr/bin/php
<?php
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "error-output.txt", "a")
);
$process = proc_open('/path/to/python/script.py', $descriptorspec, $pipes);
$input = 20;
$exp = 2;
if (is_resource($process)) {
print fgets($pipes[1]);
fwrite($pipes[0], $input . "\n");
print fgets($pipes[1]);
fwrite($pipes[0], $exp . "\n");
print fgets($pipes[1]);
fclose($pipes[1]);
fclose($pipes[0]);
$return_value = proc_close($process);
echo "command returned $return_value\n";
} else {
echo "No resource availeble";
}
And with those changes, I can make your code work as expected:
$ ./procopen.php
enter input
enter exponent
202
command returned 0
You're misunderstanding what proc_open() does. It opens a process like you would from the command line, e.g. (in Unix) diff file1.txt file2.txt. proc_open() does not run an executable.
According to the page I think you got your code from, http://php.net/manual/en/function.proc-open.php, to execute an external program you would use exec(). You can use this (provided the executable is in the right directory):
<?php
echo exec('someprogram.exe');
?>
Note: I am not certain that this will work with a Windows executable.
This is assuming that someprogram.exe is a compiled executable made from the C++ source you posted. If you wanted to run a Python program from PHP, first of all good luck using Windows, but you would want to use proc_open() to call python somescript.py, just like you would do from the command line.
Specify the absolute path of the executable file:
$process = proc_open('/path/to/new.exe', $descriptorspec, $pipes);
I can get PHP to talk to Perl with proc_open by calling the PHP script below via command line:
#!/usr/bin/php -q
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "error.log", "a")
);
$process = proc_open('/path/to/procopen.pl ' . $argv[1],
$descriptorspec, $pipes);
if (is_resource($process)) {
// print pipe output
echo stream_get_contents($pipes[1]);
// close pipe
fclose($pipes[1]);
// close process
proc_close($process);
}
Here's my Perl script which is in same directory as the PHP script above:
#!/usr/bin/perl
print #ARGV;
But I can't get the PHP to run when calling your Python script. The command line just stalls forever. Why is this?

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