Basically my situation is as follows.
Upload file
Run external process on file (which generates another file)
When external process is down, process the generated file.
Currently in PHP I run the program as follows:
$cmd = 'cd the_directory/; ./the_program'
system($cmd);
The program runs fine and everything, but the program at the end says "Press Enter to exit..." And thus Apache is hanging indefinitely as this program is waiting for user input. Our partner declares that they have this program integrated into their backend flawlessly and does not experience this issue. Up until now all external programs that i have executed in PHP exit without requiring user input which seems to be the norm for this situation.
It seems to me that the code should just simply not have the end message requiring user input. Am I missing something? Or is there a way to get around this? Or do they just need to change their code?
Thanks!
I think you should try proc_open.
With it you can not only execute an external command as a process, but also set pipes to get and send information to that process.
Take a close look to the third parameter of this function, and study the example in the PHP manual for this function, where you can see something like this:
fwrite($pipes[0], '<?php print_r($_ENV); ?>');
so, you can write what you need to the input pipe of the process you've just opened.
if you use Windows environment:
$run_cmd = "cmd /c c:/app_folder/app.exe";
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run($run_cmd, 0, false);
Note that the latter will cause the process window to close on end
If you use UNIX (haven't tested it myself yet):
exec('\app_folder\app &');
Related
I have 2 websites, hosted on 2 different servers. They are kind of interlinked. Sometimes I just do stuff on Website-1 and run a script on Website-2. Like I edited something on Website-1 and now I want to run a script on Website-2 to update accordingly on it's server.
Till now I am using following code on website 1.
$file = file_get_contents('Website-2/update.php');
But the problem with this is that my Website-1 server script stops running and wait for the file to return some data. And I don't wanna do anything with that data. I just wanted to run the script.
Is there a way where I can do this in a better way or tell PHP to move to next line of code.
If you want to call the second site without making your user wait for a response,
I would recommend using a message queue.
Site 1 request would put a message to the queue.
Cron job to check queue and run update on site 2 when message exists.
Common queues apps to look at:
[https://aws.amazon.com/sqs/?nc2=h_m1][1]
[https://beanstalkd.github.io/][2]
[https://www.iron.io/mq][3]
[1]: https://aws.amazon.com/sqs/?nc2=h_m1
[2]: https://beanstalkd.github.io/
[3]: https://www.iron.io/mq
What you're trying to achieve is called a web hook and should be implemented with proper authentication, so that not anybody can execute your scripts at any time and overload your server.
On server 2 you need to execute your script asynchronously via workers, threads, message queues or similar.
You can also run the asynchronous command on your server 1. There are many ways to achieve this. Here are some links with more on this.
(Async curl request in PHP)
(https://segment.com/blog/how-to-make-async-requests-in-php/)
Call your remote server as normal. But, In the PHP script you normally call, Take all the functionality and put it in a third script. Then from the old script call the new one with (on Linux)
exec('php -f "{path to new script}.php" $args > /dev/null &');
The & at the end makes this a background or non-blocking call. Because you call it from the remote sever you don't have to change anything on the calling server. The php -f runs a php file. The > /dev/null sends the output from that file to the garbage.
On windows you can use COM and WScript.Shell to do the same thing
$WshShell = new \COM('WScript.Shell');
$oExec = $WshShell->Run('cmd /C php {path to new script}.php', 0, false);
You may want to use escapeshellarg on the filename and any arguments supplied.
So it will look like this
Server1 calls Server2
Script that was called (on Server2) runs exec and kicks off a background job (Server2) then exits
Server1 continues as normal
Server2 continues the background process
So using your example instead of calling:
file_get_contents('Website-2/update.php');
You will call
file_get_contents('Website-2/update_kickstart.php');
In update_kickstart.php put this code
<?php
exec('php -f "{path}update.php" > /dev/null &');
Which will run update.php as a separate background (non-blocking) call. Because it's non-blocking update_kickstart.php will finish and return to searver1 which can go about it's business and update.php will run on server2 independantly
Simple...
The last note is that file_get_contents is a poor choice. I would use SSH and probably PHPSecLib2.0 to connect to server2 and run the exec command directly with a user that has access only to that file(Chroot it or something similar). As it is anyone can call that file and run it. With it behind a SSH login it's protected, with it Chrooted that "special" user can only run that one file.
I have a PHP script which sends messages to a list of users, which I hosted in Heroku. Now I wanted to add a delay in between those messages. Say like, 6 mins gap between each message. So if there are 90 users, that script should run for 9 hours, in the background.
I tried calling this script using ajax, so it runs in the background and adding sleep(360); inside the for loop, to get 6 mins delay. But it only works for approx 10 to 20 users, after that it stops.
foreach ($users_list["users"] as $key => $value) {
try{
....
code for sending message
.....
}catch(Exception $e){
continue;
}
sleep(360);
}
So I would like to know, what is the optimal way to achieve this, in Heroku.
Calling a script using AJAX doesn't call it in the "background" it just runs it Asynchronously from the page you are on. In other words it's still running in Apache, has any session data, and still bound by the timeout settings of PHP and Apache.
To run it truly in the background you can use something like CRON
Or if you are allowed to on your server you can call it by command line like with exec or shell_exec, there are a few other similar functions too, such as popen, system etc. They all do things in a slightly different way.
Some environmental stuff will be different and this may have a big impact on your code. For example a lot of stuff in $_SERVER is not set or has different information. Such as the servers IP address may not be in there, you won't have any session stuff. You won't be able to use $_GET or $_POST but can get the input data (form the command line call) from the $argv array, the first item being the files path... etc...
Basically you need to call it like this:
exec('php -f "path/to/php/file.php" "arg1" "arg2"');
Calling it this way it will still be blocking, meaning it waits for execution of the called script.
To go one step further and make it non-blockin you can add (on Linux)
exec('php -f "path/to/php/file.php" "arg1" "arg2" > /dev/null &');
The & at the end is the most important bit.
Now on Windows it's a bit of a different ballgame. I've had success using this
$WshShell = new \COM('WScript.Shell');
$cmd = 'cmd /C php "path/to/php/file.php" "arg1" "arg2"';
$WshShell->Run($cmd, 0, false);
Also on windows to run PHP with just php you have to add the path to the php.exe yhou want to use to the path environmental variable. Otherwise you have to use the full path to the exe instead of just php
In either case you should be very careful about putting end user data in any command line call. There are 2 functions to sanitize it, but I try to just not put it in.
escapeshellarg
escapeshellcmd
I wrote a wrapper class for this you can find on my GitHub
Hope it helps.
This question already has answers here:
How do you run a .bat file from PHP?
(7 answers)
php How do I start an external program running - Having trouble with system and exec
(3 answers)
Closed 3 years ago.
Before everyone starts butting in with "security risks" "cant be done" stop there and read the ENTIRE post
I have a web server set up from a home laptop which is serving as a games web server im trying to create a GUI so its easier for us to maintain the server and im trying to use batch files to do the actions on the computer
So to put this into perspective I have my index file index.php
<form method="post">
<input type="submit" name="startServer" value="Start Server">
</form>
<?
if(isset($_POST['startServer'])){
exec('batch/startServer.bat');
}
?>
And my startServer.bat will run on the laptop running the server and will do all the actions nesscary to start our game server so there is another directory "Instance" containing an excutable "Server.exe" which the batch file will run
The issue im having is running the web server and testing this it doesnt work if I open the batch file directly it works but it seems the php code doesnt work
For clarification I am using apache and my browser is chrome
And just a quick question for anyone willing to answer the route im going is correct right? Using php would allow everything to run on the machine hosting the server so the end user will only see the GUI and the server would run the batch files and everything on the web server and not the local machine if that makes sense?
EDIT: To be more clear about what's going on the function exec runs but it just hangs like the application is loading I need a solution that will actually open the application are my host computer for example if I wanted to open up notepad I press a button on the Web server and notepad will open on the computer
EDIT 2: I would like to note that I dont exactly need to use the exec function and I have tried all the answers to date 7/19/2017:3:45pm none are working if I do something on the sorts echo exec('start text.bat'); I will get a This is a test to show your batch is working and simply just have echo ..... in the batch file the main issue I am having is the server is not physically showing the opened file like displaying the GUI lets just take notepad for example
I can open notepad and get some return value as long as my batch file closes notepad once its finished running however the GUI for notepad is never displayed and thats very important
I read in a few articles about using apache as a service which im pretty sure I am but I do know that xaammp has suffiecient priveleges and I have checked the box that says "Allow apache to interact with desktop" however no GUI is popping up thats the main point I guess im trying to get across is I need to display the GUI not just open the file as a background service.
If it makes answering easier I am open to switching programming languages if theres one that can do what I want easier
Your theory is correct, it will run on the server however you may have issues running applications directly from php (with this method afaik it does not detach from the PHP, and the webapp "hangs" while the application is running).
Make sure: return values are printed / logged. Just an
<?php
if(isset($_POST['startServer'])){
echo exec('batch/startServer.bat');
}
?>
Could point you to the right direction. The exec function may have been disabled in your distribution.
Using
<?php
instead of
<?
is highly advised, by default short_tags are not enabled in most distributions (wamp, xamp, etc).
Set debug mode and print everything to get information about the problem:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if(isset($_POST['startServer'])){
echo exec('batch/startServer.bat');
}
?>
If you don't have any response, try a simple batch file with a "hello world" to test if it works.
Be aware, the rights and limitations are comes from the php environment, the batch file inherits the same rights running the PHP code / Apache (in case of mod_php)
In php manual about exec function, there is a note :
Note: If a program is started with this function, in order for it to
continue running in the background, the output of the program must be
redirected to a file or another output stream. Failing to do so will
cause PHP to hang until the execution of the program ends.
I think "hangs like the application is loading" is your application waiting for the bat file terminated / closed to get the output result
Let's try another approach, i found it here, the concept is to create a scheduler that execute the program you want and call it using command.
hope this help :
shell_exec('SCHTASKS /F /Create /TN _notepad /TR "notepad.exe" /SC DAILY /RU INTERACTIVE');
shell_exec('SCHTASKS /RUN /TN "_notepad"');
shell_exec('SCHTASKS /DELETE /TN "_notepad" /F');
If this doesn't work
Check whether you have declared safe_mode = Off inside php.ini
From here:
How do you run a .bat file from PHP?
Have you tried:
system("cmd /c C:[path to file]"); ?
You might need to run it via cmd, eg:
system("cmd /c C:[path to file]");
Or Try following options
1.
<?php
exec('c:\WINDOWS\system32\cmd.exe /c START C:\Program Files\VideoLAN\VLC\vlc.bat');
?>
2.
When you use the exec() function, it is as though you have a cmd terminal open and are typing commands straight to it.
Use single quotes like this $str = exec('start /B Path\to\batch.bat');
The /B means the bat will be executed in the background so the rest of the php will continue after running that line, as opposed to $str = exec('start /B /C command', $result); where command is executed and then result is stored for later use.
<?php
pclose(popen("start /B test.bat", "r")); die();
?>
i think this is a containment issue.
if you run the app under the process of php run by iiswebuser when php terminates it will close all spawned child processes in windows. there is a very quick way a command to break an application out of the child process containment using the start command.
if(isset($_POST['startServer'])){
exec('start batch/startServer.bat');
}
Diagram of containment as i explained it (simplisticly)
IIS (IIS runs as an IISUser)
php (application)
cmd.exe (batch)
using start bring it to the root of that tree
IIS (IIS runs as an IISUser)
php (application)
cmd.exe (batch)
Baim Wrong was correct in the first part of the response: you have to redirect output of the script or your PHP code will hang. Also, you have to move process in the background.
This is easy to do on *nix:
system("/usr/local/bin/shell.sh >> /tmp/log.log 2>&1 &");
I know that you can redirect the output on Windows but not sure how to move the process in the background. You should check DOS manual or try with power shell.
you can use either system or exec php function
$path = __DIR__ . '/batch/startServer.bat';
exec('cmd /c start ' . $path);
or
$path = __DIR__ . '/batch/startServer.bat';
$lastLine = system('cmd /c start ' . $path);
You are having some issue about running application directly from exec. I was having the same issue of running file using exec. It was solved by passing another parameter 2>&1.
exec('some_command 2>&1', $output);
print_r($output); // to see the response to your command
Check the values printed by output see exec function
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$output = array();
if(isset($_POST['startServer'])){
exec('batch/startServer.bat 2>&1', $output);
print_r($output);
} else {
echo "Not posted";
}
?>
I have a PHP website and I would like to execute a very long Python script in background (300 MB memory and 100 seconds). The process communication is done via database: when the Python script finishes its job, it updates a field in database and then the website renders some graphics, based on the results of the Python script.
I can execute "manually" the Python script from bash (any current directory) and it works. I would like to integrate it in PHP and I tried the function shell_exec:
shell_exec("python /full/path/to/my/script") but it's not working (I don't see any output)
Do you have any ideas or suggestions? It worths to mention that the python script is a wrapper over other polyglot tools (Java mixed with C++).
Thanks!
shell_exec returns a string, if you run it alone it won't produce any output, so you can write:
$output = shell_exec(...);
print $output;
First off set_time_limit(0); will make your script run for ever so timeout shouldn't be an issue. Second any *exec call in PHP does NOT use the PATH by default (might depend on configuration), so your script will exit without giving any info on the problem, and it quite often ends up being that it can't find the program, in this case python. So change it to:
shell_exec("/full/path/to/python /full/path/to/my/script");
If your python script is running on it's own without problems, then it's very likely this is the problem. As for the memory, I'm pretty sure PHP won't use the same memory python is using. So if it's using 300MB PHP should stay at default (say 1MB) and just wait for the end of shell_exec.
A proplem could be that your script takes longer than the server waiting time definied for a request (can be set in the php.ini or httpd.conf).
Another issue could be that the servers account does not have the right to execute or access code or files needed for your script to run.
Found this before and helped me solve my background execution problem:
function background_exec($command)
{
if(substr(php_uname(), 0, 7) == 'Windows')
{
pclose(popen('start "background_exec" ' . $command, 'r'));
}
else
{
exec($command . ' > /dev/null &');
}
}
Source:
http://www.warpturn.com/execute-a-background-process-on-windows-and-linux-with-php/
Thanks for your answers, but none of them worked :(. I decided to implement in a dirty way, using busy waiting, instead of triggering an event when a record is inserted.
I wrote a backup process that runs forever and at each iteration checks if there is something new in database. When it finds a record, it executes the script and everything is fine. The idea is that I launch the backup process from the shell.
I found that the issue when I tried this was the simple fact that I did not compile the source on the server I was running it on. By compiling on your local machine and then uploading to your server, it will be corrupted in some way. shell_exec() should work by compiling the source you are trying to run on the same server your are running the script.
I want initiate one php page as background process from another php page.
Use popen():
$command = 'php somefile.php';
pclose(popen($command,'r'));
This launches somefile.php as a background process.
This is a technique I used to get around restrictions applied by my webhost (who limited cronjobs to 15 minutes of execution time, so my backup scripts would always timeout).
exec( 'php somefile.php | /dev/null &' );
The breakdown of this line is:
exec() - PHP reference Runs the specified command, as if from the Linux Command Line.
php somefile.php: Invokes PHP to open, and run, somefile.php. This is the same behaviour as what would happen if that file was accessed through a web browser.
| ("pipe") - Sends the output of the proceeding command to a specified target. In this instance, it would "pipe" the content which would normally be read by the web browser accessing the file.
/dev/null - A blackhole. No, not kidding. It is a place where you send output if you just want it to disappear.
& - Appending this character to the end of a Linux command means "Do not wait - Send this to the background and continue."
So, in summary, the provided code will execute a PHP script, return no output, and not wait for it to finish before continuing onto the next line.
(And, as always, if any of these assumptions on my part are in error, I would love to be corrected by more knowledgeable members of the community.)
You have to make sure, that the background process is not terminated when the processing of the page finished. If you are on a Linux system, you could try to use the nohup command:
$command = 'nohup php somefile.php';
pclose(popen($command,'r'));
If it still gets terminated, you could try the "daemon" command.