I'm developing a little GUI to interact with mpsyt through browser. I wrote this script:
<?php
file_put_contents("running.txt", true);
$cwd = '/var/www/html';
$pipes = array();
$descriptors = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w"));
$process = proc_open("sudo mpsyt", $descriptors, $pipes, $cwd);
$info = proc_get_status($process);
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
$terminate = false;
while (!$terminate) {
$command = file_get_contents("command.txt");
if ($command !== "") {
if ($command == "terminate") {
$terminate = true;
}
if ($command == "pause") {
fwrite($pipes[0], " "); //<---HERE
} else {
fwrite($pipes[0], $command);
}
fflush($pipes[0]);
file_put_contents("command.txt", "");
}
if (($txt = stream_get_contents($pipes[1])) != "") {
$txt = preg_replace('#\R+#', '</p><p>', $txt);
file_put_contents("content.txt", $txt);
}
usleep(100000);
}
file_put_contents("content.txt", "");
fclose($pipe[0]);
fclose($pipe[1]);
proc_close($process);
file_put_contents("running.txt", false);
The problem is that mpsyt require to press spacebar to pause a song. How can i simulate this action with fwrite() (or else)?
Googling, I found this thread (same question):
https://github.com/mps-youtube/mps-youtube/issues/568
with this advertisement:
https://github.com/mpv-player/mpv/commit/a037f7b
Is there any way to bypass this situation?
Thanks in advance!
P.S. I also tried to send the SIGINT signal (if you press Ctrl-C while is playing a song, it will stop) but it seems the process ignores it.
Related
I have a simple php page that streams ping results. It works fine but when the user closes the connection, the ping command keeps running in the background. I used both ignore_user_abort() and register_shutdown_function(), but none worked.
It is running on OpenWRT with uhttpd.
this is my code in a class-based approach.
class process
{
private $proc;
private $pid;
private $descriptorspec;
function start($cmd)
{
$cwd = '/tmp';
$this->proc = proc_open($cmd, $this->descriptorspec, $pipes, $cwd);
$this->pid = proc_get_status($this->proc)['pid'];
session_write_close();
ignore_user_abort(true);
set_time_limit(0);
ini_set('zlib.output_compression', 0);
ini_set('implicit_flush', 1);
while (!feof($pipes[1])) {
echo ">" . fread($pipes[1], 4096);
flush();
ob_flush();
while (ob_get_level()) {
ob_end_clean();
}
if (connection_status() != CONNECTION_NORMAL) {
shell_exec("killall ping &");
break;
}
}
pclose($this->proc);
}
function __construct()
{
register_shutdown_function(array($this, "onShutdown"));
$this->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/app/proc.error.output.txt", "a") // stderr is a file to write to
);
}
function onShutdown()
{
shell_exec("killall ping &");
}
function __destruct()
{
shell_exec("killall ping");
// posix_kill($this->pid, SIGKILL);
// proc_terminate($this->proc);
// pclose($this->proc);
}
}
This is my function-based approach.
global $proc, $pid;
while (# ob_end_flush()) ;
$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/app/proc.error.output.txt", "a") // stderr is a file to write to
);
$cwd = '/tmp';
$proc = proc_open($cmd, $descriptorspec, $pipes, $cwd);
$pid = proc_get_status($proc)['pid'];
session_write_close();
ignore_user_abort(true);
set_time_limit(0);
register_shutdown_function('on_shutdown');
while (ob_get_level()){
ob_get_contents();
ob_end_clean();
}
//ob_start();
while (!feof($pipes[1])) {
echo fread($pipes[1], 4096);
flush();
ob_flush();
if (connection_aborted() != 0) {
posix_kill($pid, SIGKILL);
pclose($proc);
}
}
//ob_end_flush();
pclose($proc);
Check phpinfo for the environment setup, set PATHs, if shell_exec is disabled, or FPM runs in a chroot.
I am having issues with my php code. I am executing c++rom fffff code using proc_open() in php.
//from another function I'm calling this function with parameters execute("code.exe", $STDIN, 0.02, 268435456 * 2);
//my c++ code is already executed to .exe file.
function execute($cmd, $stdin, $timeout, $memoryout)
{
$descriptorspec = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']];
$timer = microtime(true);
$TL = false;
$ML = 0;
$stdout = '';
$process = proc_open($cmd, $descriptorspec, $pipes);
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
while(!feof($pipes[1]))
{
if($timer + $timeout < microtime(true))
{
$TL = true;
break;
}
$ML = max($ML, memory_get_usage());
$stdout .= fread($pipes[1], 1);// . "<br/>";
if($timer + $timeout < microtime(true))
{
$TL = true;
break;
}
}
$status = proc_get_status($process);
if(!$status['running'])
if($status['exitcode'] !== 0) $stdout = 'code:RE';
if($TL) $stdout = 'code:TL';
if($ML > $memoryout) $stdout = 'code:ML';
$pid = proc_get_status($process)['pid'];
strstr(PHP_OS, 'WIN') ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
fclose($pipes[1]);
proc_close($process);
return $stdout;
}
So, when my c++ code is correctly written, there is no problem, but when i've written something like "while(1);" in my c++ code, my php code does not terminate. while(!feof($pipes[1])) cycle loops forever and I don't know what to do.
I've tried to use getrusage() instead of time stamp but it was worse.
In timer.php I have this:
$handle = fopen( 'php://stdout', 'wa' ) ;
$unusedEvTimerObject = new EvTimer(0, 1, function ($watchercallback) use ($handle) { //create & call timer
echo "=>".(Ev::iteration() % 60)."<=";
fwrite( $handle, "Hello World! \n");
} );
Ev::run();
fclose( $handle );
And in child.php I have this:
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a")
);
$process = proc_open('php ', $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "<? include('app/timer.php'); ?>");
fclose($pipes[0]);
$output = "";
while (!feof($pipes[1])) {
$output .= fgets($pipes[1]);
};
fclose($pipes[1]);
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
If I invoke timer.php direct with
$php app/timer.php
the output I get is "=>1<=Hello World! =>2<=Hello World!"
but if I invoke with $php app/child.php
I get no output whereas I'd expect stdout from timer.php to redirect to child.php and be printed by it.
I'm flailing a bit & I'm guessing child.php is not getting any input but I can't see why. HALP!
The $output isn't printed in the sample code.
while (!feof($pipes[1])) {
echo fgets($pipes[1]);
};
Calling $> php child.php then prints the timer output
This is a server client web socket program, when client send "exe" command the server executes a C application.
The output of the c application is read from STDOUT by server and displayed on the client browser.
Now there is a C application that requires a user input(scanf), we saved the input in a txt file. We read the input
and write it to the stdin of the c program. The problem is that it seems that the C program is not accepting the input
that we are writting on the stdin. When i tried this code without any stream select it was working and displaying the sum of two numbers read from input file, but when i place it in stream select command its creating problem. My complete server code is at the end.
a part of server code that is creating child process on a client request
if(strcmp($user_message, "exe") == 0 ) {
echo "Executing a process\n";
$cwd = '/var/www/html/test/websockets' ;
$process = proc_open($exe_command, $descriptorspec, $pipes, $cwd);//creating child process
sleep(1);
if (is_resource($process))
{
echo "Process Created";
$read_socks[] = $pipes[1];//add a descriptor
$stdout = array($pipes[1]);//save stdout in a variable defined above
$stdin = array($pipes[0]);
print_r ($stdout);
print_r ($stdin);
}
}
else
{
echo "Passing value to the C program".$user_message;
//Read input.txt by line and store it in an array
$input = array();
$input = file('/var/www/html/test/websockets/input.txt');
echo "INPUT";
print_r($input);
echo "\n";
//Feed the input (hardcoded)
$bytes = fwrite($stdin[0], "$input[0] $input[1]");
echo "Bytes written:".$bytes;
sleep(1);
}
}
}
add.c
#include <stdio.h>
int main(void)
{
int first, second;
printf("Enter two integers > \n");
scanf("%d", &first);
scanf("%d", &second);
printf("The two numbers are: %d %d\n", first, second);
printf("Output: %d\n", first+second);
}
input.txt
2
4
output
Passing value to the C program3Array
(
[0] => Resource id #11
)
INPUTArray
(
[0] => 2
[1] => 4
)
Bytes written:5
By doing ps I get
root 1173 1164 0 12:08 pts/6 00:00:00 tclsh /usr/bin/unbuffer /var/www/html/test/websockets/./add
root 1174 1173 0 12:08 pts/8 00:00:00 /var/www/html/test/websockets/./add
After bytes written the server is hanging there is no response from the C application i.e the addition result of 4+2.
Why there are two process of add i started 1 with proc open.
complete server code
<?php
execute_prog('unbuffer /var/www/html/test/websockets/./add');//unbuffer stdout
function execute_prog($exe)
{
echo "[+execute_prog]";
$host = 'localhost'; //host
$port = '9000'; //port
$null = NULL; //null var
$read_socks;
$new_client;
$server = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errorMessage);
if ($server === false)
{
throw new UnexpectedValueException("Could not bind to socket: $errorMessage");
}
set_time_limit(1800);
$exe_command = escapeshellcmd($exe);
$descriptorspec = array(
0 => array("pipe", "r"), // stdin -> for execution
1 => array("pipe", "w"), // stdout -> for execution
2 => array("pipe", "w") // stderr
);
// $process = proc_open($exe_command, $descriptorspec, $pipes);//creating child process
// if (is_resource($process))
{
$client_socks = array();
$read_socks = array($server);
$changed = array();
$stdout = NULL;
$stdin = NULL;
while(1)
{
//prepare readable sockets
$write = NULL;
$err = NULL;
$except = NULL;
$changed = $read_socks;//by refrence
/*
echo "stdout:";
print_r ($stdout);
echo "\n";
echo "changed:";
print_r ($changed);
echo "\n";
echo "read sock:";
print_r ($read_socks);
echo "\n";
*/
if (false === ($num_changed_streams = stream_select($changed, $write, $except, 0)))
{
/* Error handling */
echo "Errors\n";
}
else if ($num_changed_streams > 0)
{
/* At least on one of the streams something interesting happened */
echo "Data on ".$num_changed_streams." descriptor\n";
if(in_array($stdout[0], $changed))
{
echo "Data on child process STDOUT\n";
$s = fgets($stdout[0]);
if( $s === false )
{
// Hello program has finished.
echo 'Finished', PHP_EOL;
$s = NULL;
//ob_flush();
flush();
// Close all descriptors and return...
// break;
}
else
{
echo $s."</br>";
//prepare data to be sent to client
$response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$s, 'color'=>$user_color)));
foreach ($read_socks as $sock)
{
if(($sock != $server) && ($sock != $stdout))
fwrite($sock, $response_text, strlen($response_text));
}
$s = NULL;
//ob_flush();
flush();
}
}
else if(in_array($server, $changed))
{
//new client
echo "New Connection\n";
$new_client = stream_socket_accept($server);
if ($new_client)
{
//print remote client information, ip and port number
echo 'Connection accepted from ' . stream_socket_get_name($new_client, true) . "n";
$read_socks[] = $new_client;
echo "Now there are total ". count($read_socks) . " clients.n";
}
$header = fread($new_client, 1024);//read data sent by the socket
perform_handshaking($header, $new_client, $host, $port); //perform websocket handshake
$ip = stream_socket_get_name($new_client, true);
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
fwrite($new_client,$response,strlen($response));
//delete the server socket from the read sockets
unset($changed[ array_search($server, $changed) ]);
}
else if($write)
{
echo "Data on child process STDIN\n";
}
else if($err)
{
echo "Data on child process STDERR\n";
}
else
{
echo "Message from the client \n";
//message from existing client
foreach($changed as $sock)
{
$data = fread($sock, 128);
//echo "Data read:".$data." From sock:".$sock."\n";
if(!$data)
{
unset($client_socks[ array_search($sock, $client_socks) ]);
#fclose($sock);
echo "A client disconnected. Now there are total ". count($client_socks) . " clients.n";
continue;
}
else
{
$received_text = unmask($data); //unmask data
$tst_msg = json_decode($received_text); //json decode
$user_name = $tst_msg->name; //sender name
$user_message = $tst_msg->message; //message text
$user_color = $tst_msg->color; //color
//echo "name:".$user_name." user mesg:".$user_message."\n";
//prepare data to be sent to client
$response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color)));
fwrite($sock, $response_text, strlen($response_text));
//..................................................................................................................
if(strcmp($user_message, "exe") == 0 )
{
echo "Executing a process\n";
$cwd = '/var/www/html/test/websockets' ;
$process = proc_open($exe_command, $descriptorspec, $pipes, $cwd);//creating child process
sleep(1);
if (is_resource($process))
{
echo "Process Created";
$read_socks[] = $pipes[1];//add a descriptor
$stdout = array($pipes[1]);//save stdout in a variable defined above
$stdin = array($pipes[0]);
print_r ($stdout);
print_r ($stdin);
}
}
else
{
echo "Passing value to the C program".$user_message;
print_r ($stdin);
echo "\n";
//$input = array($user_message);
// fwrite($stdin[0],"$input[0]");//,strlen($user_message));
//Read input.txt by line and store it in an array
$input = array();
$input = file('/var/www/html/test/websockets/input.txt');
echo "INPUT";
print_r($input);
echo "\n";
//Feed the input (hardcoded)
$bytes = fwrite($stdin[0], "$input[0] $input[1]");// $input[1]");
echo "Bytes written:".$bytes;
}
}
}
}
$num_changed_streams = 0;
}
}
// close the listening socket
fclose($server);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
echo "exitcode: ".proc_close($process)."\n";
}
echo "[-execute_prog]";
// return $ret;
}
?>
Any ideas how to solve this?
Thank you!
EDIT
The following code writes to the stdin of a C application but when i tried to integrate in the above client server its not working.
<?php
//descriptors to be handled by parent
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/var/www/html/websockets/error.txt", "a")
);
// define current working directory where files would be stored
$cwd = '/var/www/html' ;
$child_proc = escapeshellcmd("unbuffer /var/www/html/test/websockets/./add");
// $process = proc_open('/var/www/html/websockets/add', $descriptorspec, $pipes);//creating child process
$process = proc_open($child_proc, $descriptorspec, $pipes, $cwd);//creating child process
sleep(1);
if (is_resource($process))
{
//Read input.txt by line and store it in an array
$input = file('/var/www/html/test/websockets/input.txt');
//Feed the input (hardcoded)
fwrite($pipes[0], "$input[0] $input[1]");
fclose($pipes[0]);
while ($s = fgets($pipes[1]))
{
print $s."</br>";
flush();
}
?>
Any guess why the code in the edit part is working where as when integrated in the client server application its output is not as expected.
$child_proc = escapeshellcmd("unbuffer /var/www/html/test/websockets/./add")
the keyword unbuffer was causing the problem although it was there for some other reason but it was the one creating problem.
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?