Run node file.js from php and kill it on unload - Windows - php

my goal is to run a node file from my php page at the start of my script and then kill that process when the page is closed. I have been reading several questions and answers without luck. I can run by example
exec("node myapp.js arg1 arg2 2>&1", $retArr);
In my node file i added:
console.log(process.pid);
So now i receive the PID of the process on the output array but have to be ended, with this i could kill it later with exec('taskkill /PID '.$array[x].' /f'), how i can get that process pid from php to kill only that one?.
Any lights will be REALLY appreciated. Thanks in advance.

Windows Users:
Ok I found a workaround for this situation in case someone face the same issue.
In PHP
The node app need to listen in a defined port, so you can pass that port to the node app as argument and I needed to start that node app in the background at the beginning of my PHP, from all the possible solutions this one was the one that worked best for me:
$rshell = new COM("WScript.Shell");
$rexec = $rshell->Run("node C:\PATH\TO\NODE\APP\myapp.js ".$appPort." ", 0, false);
this will run the app in the background and you have the listening port in a variable.
In Node.js
Node apps can receive arguments and you can retrieve them using:
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`);
});
var appPort = process.argv[2];
...
app.listen(appPort);
Back to the PHP
Fine, so far you can start up the node app from php in the background and you have the listening port, so now we create a new php file to handle the closing process, lets say closeNode.php that will receive our listening variable via AJAX onunload :
$( window ).unload(function() {
<!-- your ajax call sending the appPort variable-->
});
closingNode.php
$appPort = $_POST('appPort')
exec('netstat -a -n -o | find "LISTENING" | find "'.$appPort.'"', $result);
if (!empty($result)) {
$pidArray = array();
foreach ($result as $value) {
// our pid is at the end of the string, then we trim the last 10 characters that is enough for the pid process number
$pidArray[] = substr($value, -10);
}
$pidTrimmed = array_map('trim', $pidArray);
$pid = $pidTrimmed[0];
exec('taskkill /f /PID '.$pid.'', $resultKill);
Maybe is not the more elegant solution, but it works.

Related

PHP proc_open() with timeout

I want to call proc_open to execute a script in the background, and the background process will terminate after a few seconds. Basically, the script is a C/Java/Python script that will compile and run the user submitted code, so I want the process to be able to be terminated after some time.
What I want to achieve is that when the execution time of the background running script exceeds, say 3 seconds, halt the process as well as stop writing to the file. Let's say I run a for loop to write 1 million lines of some string to a file, and at time >= 3 seconds, the process stops. When I retrieve back the file, I will get like 200k lines of string. Then I can display the output of the file back to the browser.
I am currently using the function exec_timeout from https://blog.dubbelboer.com/2012/08/24/execute-with-timeout.html.
Then I execute a command exec_timeout("exec nohup java -cp some_dir compiled_java_file &", 3), the background process will not be terminated even if it already exceeds the timeout value, instead it will continue to write to the file until it completes. Then only I can echo the result back to the browser. If the user submits a infinite running code, the process would just hanging there until I kill it in ec2 linux instance.
Any idea of why it is not functioning as expected? Or any better function available to achieve my goal? My application is developed in PHP and hosted on AWS Elastic Beanstalk.
On proc_terminate manual, first user contributed notes:
As explained in http://bugs.php.net/bug.php?id=39992, proc_terminate()
leaves children of the child process running. In my application, these
children often have infinite loops, so I need a sure way to kill
processes created with proc_open(). When I call proc_terminate(), the
/bin/sh process is killed, but the child with the infinite loop is
left running.
On exec_timeout:
proc_terminate($process, 9);
should be replaced by:
$status = proc_get_status($process);
if($status['running'] == true) { //process ran too long, kill it
//get the parent pid of the process we want to kill
$ppid = $status['pid'];
//use ps to get all the children of this process, and kill them
$pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`);
foreach($pids as $pid) {
if(is_numeric($pid)) {
echo "Killing $pid\n";
posix_kill($pid, 9); //9 is the SIGKILL signal
}
}
proc_close($process);
}

php exec in the background with WAMP on Windows

with the following code i can call a php script and pass some variables into it
$cmd = 'php -f C:/wamp/www/np/myphpscript.php '.$var1;
exec($cmd);
this way my called script works, but , i need that process to be in the background , i dont want to wait for the script to finish, is there any way of doing that using wamp on windows ?
been doing some reading and some add a & at the end of the command, or a > NUL , now i noticed some of them are for linux , is there such a command for wamp on windows ? if there is please share it
EDIT: Due to the way the exec() command waits for the program to finish executing, it's very difficult to do this with vanilla exec(). I came across these solutions, and this one should work:
$rshell = new COM("WScript.Shell");
$rexec = $rshell->Run("php -f C:/wamp/www/np/myphpscript.php ".$var1, 0, false);
The WScript.Shell->Run command takes 3 arguments: the command (you can optionally add output redirection), window mode (0 = hidden), and wait it should wait to finish. Because the 3rd argument is false, this PHP should return immediately.
Original Solution: As this post suggests, you should try START /B cmd. It is virtually the Linux equivalent of cmd & in that it runs the command asynchronously, in the background, without user interaction or opening a new shell.
Because this will return immediately, PHP won't wait for it to finish, and the exec() command will not receive any output. Instead, try using shell output redirection. Your PHP given code would look like this:
$cmd = 'start /b "" php -f C:/wamp/www/np/myphpscript.php '.$var1.' >C:/wamp/www/np/output.txt';
exec($cmd);
Don't know what you are running and if you get a response to your command. But maybe it helps if you open a tab for each command. So you can see responses of each running script and at the end you can call javascript to close the tab.
You must set the variable php on windows environment !
If you have already done so skip the tutorials steps:
1. Open:
My Computer => Properties => Change Settings
2. Select the tab: Advanced
3. Click Environment Variables: Variable system
4. Click the button New
Add the name of the environment variable. Example = php
Add the path to executable php.exe. Example = D:\xampp\php\php.exe
Create a file myscript.php
The variariaveis $argc and $argv are native php.
You will notice that $ argc always carries the same value as the
result of calling count ($argv) in any case $argc is the standard
used and is a few milliseconds faster by being in memory (if that
makes any difference in performance your script).
//\n skip line
echo "\n\n";
//echo test debug
echo "Print Total Args : ";
//Print return variavel $argc
print_r($argc);
//\n skip line
echo "\n\n";
//echo test debug
echo "Print Array Args : \n\n";
//Print return variavel $argv
print_r($argv);
echo "\n";
// You can retrieve the arguments in the normal way.
$myvar_count = $argc;
$myvar_array_args = $argv;
Or if you want to set is not the environment variable, simply can call the path
Example: D:\xampp\php\php.exe myscript.php argument1 2 3 4 5
Retorn the Prompt in Windows
Total Args : 5
Array Args :
Array
(
[0] => test.php
[1] => argumento1
[2] => 2
[3] => 3
[4] => 4
)
I hope this helps! See you later!

advance process control in PHP

I need to build a system that a user will send file to the server
then php will run a command-line tool using system() ( example tool.exe userfile )
i need a way to see the pid of the process to know the user that have start the tool
and a way to know when the tool have stop .
Is this possible on a Windows vista Machine , I can't move to a Linux Server .
besides that the code must continue run when the user close the browser windows
Rather than trying to obtain the ID of a process and monitor how long it runs, I think that what you want to do is have a "wrapper" process that handles pre/post-processing, such as logging or database manipulation.
The first step to the is to create an asynchronous process, that will run independently of the parent and allow it to be started by a call to a web page.
To do this on Windows, we use WshShell:
$cmdToExecute = "tool.exe \"$userfile\"";
$WshShell = new COM("WScript.Shell");
$result = $WshShell->Run($cmdToExecute, 0, FALSE);
...and (for completeness) if we want to do it on *nix, we append > /dev/null 2>&1 & to the command:
$cmdToExecute = "/usr/bin/tool \"$userfile\"";
exec("$cmdToExecute > /dev/null 2>&1 &");
So, now you know how to start an external process that will not block your script, and will continue execution after your script has finished. But this doesn't complete the picture - because you want to track the start and end times of the external process. This is quite simple - we just wrap it in a little PHP script, which we shall call...
wrapper.php
<?php
// Fetch the arguments we need to pass on to the external tool
$userfile = $argv[1];
// Do any necessary pre-processing of the file here
$startTime = microtime(TRUE);
// Execute the external program
exec("C:/path/to/tool.exe \"$userfile\"");
// By the time we get here, the external tool has finished - because
// we know that a standard call to exec() will block until the called
// process finishes
$endTime = microtime(TRUE);
// Log the times etc and do any post processing here
So instead of executing the tool directly, we make our command in the main script:
$cmdToExecute = "php wrapper.php \"$userfile\"";
...and we should have a finely controllable solution for what you want to do.
N.B. Don't forget to escapeshellarg() where necessary!

Passing a variable through to another PHP page

I'm making a server manager. I want to add a "kill" command, which would call a php script that would essentially run a shell_exec('kill $arrtext'); and kill the process, thus closing the server down. Here is the part of my script that returns the results and checks to see which servers are running:
<?php
$COMMAND = shell_exec('ps -o command ax | grep skulltag | grep -v grep');
$old = array("skulltag-server", "-port", "-iwad", "-wad", "+exec");
$new = array("SKULLTAG", "Running on Port", "Using IWAD", "+ PWAD", "with Config");
$COMMAND = str_replace($old, $new, $COMMAND);
$arr = explode("./",$COMMAND);
$text = shell_exec('pgrep -u doom');
$arrtext = preg_split('/\s+/', $text);
for( $i = 1; $i < count($arr); $i++ ) {
echo '<div class = "serverborder">';
echo '<div class = "servertextalign">';
echo $i,'. PROCESS ID <span style="color: #f00;">',$arrtext[$i],'</span> with server parameters: <span style="color: #777;">',$arr[$i],'</span>';
echo '</div>';
echo '</div>';
echo '<br>';
}
?>
However, I have no idea how I would add a link or something that would set the proper $arrtext[] variable (depending on which one they picked) and pass it to the PHP script that would kill the server.
The live demo can be found at http://server.oblivionro.net/servers/
Thanks!
Could you try using a shell_exec in another, tiny, script to run that kill script in the command line? Don't use a GET variable. I would rather create a small form for each server in the list and passing it through POST ie. requiring the tiny script that takes hidden POST variables, sending the form action to the same page, and passing the array as a parameter
// In form
echo '<input type="hidden" name="pid" value="'.$arrtext[$i].'"/>';
// In script
$pid = $_POST['pid'];
shell_exec('php -f /location/of/kill_script.php -- -'. $pid)
Where pid is your process ID. Obviously, you should set up your kill script to check that the pid is valid. The benefit of this is that the script's true location can stay hidden and doesn't need even need to be in the www root. You shouldn't need to link the real script directly.
The dirty, dirty (not recommended) way to do it is to have the link go to the script with the command, like:
KILL
Then in the killScript, you RIGOROUSLY verify that the process they are killing something they're supposed to be killing.
A better way would be to avoid using as powerful a command as "kill", so that someone doesn't go to killScript.php?p=1230 where 1230 is the process number of your Minecraft game or something...
I am confused why you feel the need to ask since you seem to have a grasp of PHP scripting and already create spans etc with the proper data. it is trivial to construct an anchor tag. The anchor tag might reference a GET variable, which could be the PID. After proper validation such as ensuring the PID references a doom server process (and proper login credentials), the PID can then be used to shell a kill command.
Note that you are potentially opening your server up to allow the world to shut down processes on your server.

Killing processes opened with popen()?

I'm opening a long-running process with popen(). For debugging, I'd like to terminate the process before it has completed. Calling pclose() just blocks until the child completes.
How can I kill the process? I don't see any easy way to get the pid out of the resource that popen() returns so that I can send it a signal.
I suppose I could do something kludgey and try to fudge the pid into the output using some sort of command-line hackery...
Well, landed on a solution: I switched back to proc_open() instead of popen(). Then it's as simple as:
$s = proc_get_status($p);
posix_kill($s['pid'], SIGKILL);
proc_close($p);
Just send a kill (or abort) signal using kill function:
php http://php.net/manual/en/function.posix-kill.php
c/c++ http://linux.die.net/man/3/kill
You can find the pid, and checks that you're really its parent by doing:
// Find child processes according to current pid
$res = trim(exec('ps -eo pid,ppid |grep "'.getmypid().'" |head -n2 |tail -n1'));
if (preg_match('~^(\d+)\s+(\d+)$~', $res, $pid) !== 0 && (int) $pid[2] === getmypid())
{
// I'm the parent PID, just send a KILL
posix_kill((int) $pid[1], 9);
}
It's working quite well on a fast-cgi PHP server.

Categories