How to run a background process by PHP in windows? - php

I try to run a Python script as a background process from PHP on Windows using exec() on this way:
<?PHP
$python = 'C:\\Users\\User\\anaconda3\\python.exe';
$py_script = 'C:\\wamp\\www\\lab\\ex\\simple_test.py';
$py_stdout = '> temp\\'.session_id()."_std.txt";
$py_stderror = '2> temp\\'.session_id()."_stde.txt";
exec("$py_bg $python $py_script $py_stdout $py_stderror &");
The script called and worked correctly, but PHP still waiting for the script.
I removed the end & as I foundout it's only work on Linux and after searching other Q&A find this sulotion:
exec("start /B $py_bg $python $py_script $py_stdout $py_stderror");
But same result. How can I solve this problem?
=== UPDATE:
I used start /B in the wrong way, I changed my code to this:
<?PHP
$python = 'C:\\Users\\User\\anaconda3\\python.exe';
$py_script = 'C:\\wamp\\www\\lab\\ex\\simple_test.py';
$py_stdout = '> temp\\'.session_id()."_std.txt";
$py_stderror = '2> temp\\'.session_id()."_stde.txt";
$py_cmd = "$python $py_script $py_arg_1 $py_std $py_stde";
pclose(popen("start /B ". $py_cmd, "a"));
But now a Warning in PHP for popen():
Warning: popen(start /B ...,a): No error in C:\wamp\www\lab\start.php on line 50
and an other for pclose():
Warning: pclose() expects parameter 1 to be resource, bool given in ...

I checked PHP: popen - Manual and see there a is not a valid mode, but I see this on several answers around here!
however:
The mode. Either 'r' for reading, or 'w' for writing.
By changing mode to r, the script call and run in the background correctly and there is not an error or warning on PHP or Py.
<?PHP
$python = 'C:\\Users\\User\\anaconda3\\python.exe';
$py_script = 'C:\\wamp\\www\\lab\\ex\\simple_test.py';
$py_stdout = '> temp\\'.session_id()."_std.txt";
$py_stderror = '2> temp\\'.session_id()."_stde.txt";
$py_cmd = "$python $py_script $py_arg_1 $py_std $py_stde";
pclose(popen("start /B ". $py_cmd, "r"));

Related

Best way to open a PTY with PHP

Problem description
I would like to open a Linux pseudoterminal with PHP but it seems there is no simple way of doing that. I have experimented with different solutions but none seem to be good enough.
The goal of the PTY is to emulate a terminal with the capability of flawlessly interacting with programs such as zsh and sudo. Other programming languages including Python and C have functions or libraries for that. Python has the PTY library which can simply do pty.spawn("/bin/zsh"), and C has the openpty() function.
My ideal end goal is to have a PHP function that allows me to read and write from/into the PTY terminal and does not require installing external libraries. (A lot of shared hosting providers do not allow that.)
What I have tried so far
Using proc_open()
My initial idea was just to use the proc_open() PHP function to create a Bash terminal with stdin, stdout and stderr pipes (Based on Example #1 in the PHP documentation) This, however, did soon prove to be problematic because it is actually not a real PTY. Running stty -a errored with stty: stdin isn't a terminal. Here are the instructions for reproducing this.
Run this with php pty_test.php
Read the output of the shell with cat /tmp/stdout.
Input commands with > /tmp/stdin.
Here is the PHP code which I used for this:
<?php
/* pty_test.php */
ini_set('display_errors', 1);
ini_set('display_startup_ūūerrors', 1);
error_reporting(E_ALL);
define("STD_IN", 0);
define("STD_OUT", 1);
define("STD_ERR", 2);
set_time_limit(0);
umask(0);
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = "/bin/sh -i ";
$stdin_fifo = "/tmp/stdin";
$stdout_fifo = "/tmp/stdout";
posix_mkfifo($stdin_fifo, 0644);
posix_mkfifo($stdout_fifo, 0644);
$resource_stdin = fopen($stdin_fifo, "rb+");
$resource_stdout = fopen($stdout_fifo, "wb+");
$descriptorspec = array(
STD_IN => array("pipe", "rb"),
STD_OUT => array("pipe", "wb"),
STD_ERR => array("pipe", "wb")
);
$process = proc_open($shell, $descriptorspec, $pipes, null, $env = null);
stream_set_blocking($pipes[STD_IN], 0);
stream_set_blocking($pipes[STD_OUT], 0);
stream_set_blocking($pipes[STD_ERR], 0);
stream_set_blocking($resource_stdin, 0);
stream_set_blocking($resource_stdout, 0);
while (1) {
$read_a = array($resource_stdin, $pipes[STD_OUT], $pipes[STD_ERR]);
$num_changed_streams = stream_select($read_a, $write_a, $error_a, null);
if (in_array($resource_stdin, $read_a)) {
$input = fread($resource_stdin, $chunk_size);
fwrite($pipes[STD_IN], $input);
}
if (in_array($pipes[STD_OUT], $read_a)) {
$input = fread($pipes[STD_OUT], $chunk_size);
fwrite($resource_stdout, $input);
}
if (in_array($pipes[STD_ERR], $read_a)) {
$input = fread($pipes[STD_ERR], $chunk_size);
fwrite($resource_stdout, $input);
}
}
fclose($resource_stdin);
fclose($resource_stdout);
fclose($pipes[STD_IN]);
fclose($pipes[STD_OUT]);
fclose($pipes[STD_ERR]);
proc_close($process);
unlink($stdin_fifo);
unlink($stdout_fifo);
?>
Python PTY
I noticed that running python3 -c "import pty;pty.spawn('/bin/bash');" in the non-pty shell (which I described above) will result in a fully interactive PTY shell as I desired. This resulted in a half-good solution: setting the $shell variable to be python3 -c "import pty;pty.spawn('/bin/bash')" will spawn the interactive shell using Python3. But relying on external software is not ideal since having Python3 is not always guaranteed. (And this solution also feels way too hacky...)
/dev/ptmx
I was reading the source code of the proc_open() function also found the source for openpty(). Unfortunately, PHP can't directly call this function but perhaps it is possible to replicate the behavior of it.
I could fopen("/dev/ptmx","r+") to create a new slave but openpty() also uses grantpt() and unlockpt(), which are not available in PHP.
Foreign Function Interface
FFI allows access to external libraries. Maybe it would be possible to import pty.h and to run openpty(). Unfortunately, FFI is very experimental and may not always be available.
TL;DR
What is the safest and most reliable way to spawn a PTY using PHP?
You do not have to use FFI to write PHP shared library.
I just tried to write an open source PHP library for this purpose. I named it TeaOpenPTY. I think this can be a good example how to write a simple PHP library in C.
GitHub repo: https://github.com/ammarfaizi2/TeaOpenPTY
Precompiled Shared Lib: https://github.com/ammarfaizi2/TeaOpenPTY/raw/master/compiled/tea_openpty.so
How to use the TeaOpenPTY library?
File test.php
<?php
use TeaOpenPTY\TeaOpenPTY;
$app = "/usr/bin/bash";
$argv = [$app, "-i"];
$teaOpenPTY = new TeaOpenPTY($app);
echo "Starting TeaOpenPTY...\n";
$ret = $teaOpenPTY->exec(...$argv);
if ($ret === -1) {
echo "Error: ", $teaOpenPTY->error(), "\n";
}
echo "TeaOpenPTY terminated!\n";
Run
ammarfaizi2#integral:/tmp$ wget https://github.com/ammarfaizi2/TeaOpenPTY/raw/master/compiled/tea_openpty.so
[...output abbreviated...]
2020-12-28 14:39:20 (612 KB/s) - ‘tea_openpty.so’ saved [19048/19048]
ammarfaizi2#integral:/tmp$ echo $$ # Show the current bash PID
19068
ammarfaizi2#integral:/tmp$ php -d extension=$(pwd)/tea_openpty.so test.php
Starting TeaOpenPTY...
ammarfaizi2#integral:/tmp$ echo $$ # Now we are in the terminal spawned by tea_openpty
329423
ammarfaizi2#integral:/tmp$ stty -a
speed 38400 baud; rows 46; columns 192; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke -flusho -extproc
ammarfaizi2#integral:/tmp$ exit # Terminate the terminal
exit
TeaOpenPTY terminated!
ammarfaizi2#integral:/tmp$ echo $$
19068
ammarfaizi2#integral:/tmp$

bash echoing value to process, php loop (the process) not reading stdin

Background:
I'm in a position where I'm placing data into the command line and I need a php loop (what will become a server of sorts) to read STDIN and just echo what it reads to the shell its running in.
The following terrible code works when the process is running in the same shell as the content echoed:
<?php
echo getmypid();
$string = "/proc/" . getmypid() . "/fd/0";
while (true) {
fwrite(STDOUT, fgets(fopen($string, 'r'), 4096) . " worked\n");
}
?>
I've tried many variants:
<?php
echo getmypid();
$string = "/proc/" . getmypid() . "/fd/0";
while (true) {
$fo = fread(STDIN, 1024);
fwrite(STDOUT, $fo);
}
?>
The problem is that whenever I write to this loop from a separate terminal, the output appears in the other terminal but is not processed by the loop.
When I enter text in the same terminal, the text is echoed right back.
I need a way to get command line data into this loop from any source.

How to make a Python interpreter for a webpage

There might already be questions like this, but none of them answer my question. I have a script that loads a Python script in the directory and then displays a output with PHP:
<?php
$param1 = "first";
$param2 = "second";
$param3 = "third";
$command = "scripts/sg.py";
$command .= " $param1 $param2 $param3 2>&1";
header('Content-Type: text/html; charset=utf-8');
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
echo "<style type='text/css'>
</style>";
$pid = popen( $command,"r");
echo "<body><pre>";
while( !feof( $pid ) )
{
echo fread($pid, 256);
flush();
ob_flush();
echo "<script>window.scrollTo(0,99999);</script>";
usleep(100000);
}
pclose($pid);
echo "</pre><script>window.scrollTo(0,99999);</script>";
echo "<br /><br />Script finalizado<br /><br />";
?>
And is the Python code it should run, that is located in the directory:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Script Python Example
import time
import sys
print "Initializing Python Script"
secret = 1337
guess = 0
count = 0
#Help the user give number range.
print 'Guess s number it`s between 0 <-> 2000 '
while guess != secret:
guess = input("Guess: ")
if guess < secret:
print "to small"
elif guess > secret:
print "to big"
count += 1
print 'You guessed the number in %s try' % count
The Python actually works! However it seems that Python's inputs don't work, they create a EOF error (end of file error).
Can anybody help me and suggest a way to create a Python interpreter that runs the Python file found in the directory. Just like, skuplt.org, but instead of running code a client users, it runs a Python file located in a directory, like stated above.
popen opens a pipe that is one-way only; you can read from it or write to it, but not both.
You want to use proc_open instead - see http://php.net/manual/en/function.proc-open.php

executing a Windows command line program with system call in PHP

I am trying to execute a program using a system call inside a php file like so:
$newname = 'C:\Users\Jack\Desktop\upload\test.ppt' ;
$program = '"C:\Program Files (x86)\Softinterface, Inc\Convert PowerPoint\ConvertPPT"';
$input = ' /S "'. $newname .'"'
$destination = ' /T "C:\Users\Jack\Desktop\upload\\"';
$switch = ' /C 18';
$command = $program . $input . $destination . $switch;
system($command);
For some reason, the program enters an infinite loop (the browser never stops loading). I have tried the command manually and it works perfectly (takes about 2 sec to complete) but when executing it with a php file doesn't work.
use the backtick(`) symbol to wrap around the command you want to run.

Getting script exit status code in php

Is there anyway to get the exit status code for a php script run in the background via exec($cmd, $output, $exitCode)?
For example:
exec('php script.php &', $output, $exitCode);
If the trailing '&' isn't included then $exitCode works as expected, it's always 0 otherwise.
For anybody that finds themselves here, my solution was to make a php script that takes a script as an argument. The new script is called to the background and handles exit statuses appropriately.
For example:
$cmd = 'php asynchronous_script.php -p 1';
exec("php script_executor.php -c'$cmd' &");
The second script looks something like
$opts = getOpt('c:');
$cmd = rtrim($opts['c'], '&');
$exitCode = 0;
$output = '';
exec($cmd, $output, $exitCode);
if ($exitCode > 0) {
...
}
I found something that is probably quite equivalent to Pradeep's solution. Someone posted about this on the function's documentation page.
http://www.php.net/manual/en/function.exec.php#101506

Categories