Program execution
在线手册:中文 英文
PHP手册

Program execution 函数

注释

Warning

Open files with lock (especially open sessions) should be closed before executing a program in the background.

参见

These functions are also closely related to the backtick operator. Also, while in 安全模式 you must consider the safe_mode_exec_dir directive.

Table of Contents


Program execution
在线手册:中文 英文
PHP手册
PHP手册 - N: Program execution 函数

用户评论:

benkitzelman at yahoo dot co dot uk (20-Aug-2010 04:02)

Within Linux, when calling the php interpreter directly from popen, or other program execution function to execute a script, it appears as though the script perpetually fails and re-executes.

i.e. <?php popen("php myscript.php&", "w"); // fail ?>

As each re-execution causes the executing script to load under a new PID, it is incredibly difficult to identify and kill the process manually.

[One solution is] to ensure the executing script has a valid shebang, and execute permissions. This allows you to execute the script directly

i.e. <?php popen("./myscript.php", "w"); // ok ?>

Anonymous (09-Jul-2008 06:34)

I found this comment on this page:

[begin of quote]
Trying to us the following code failed badly with various results: like "unable to fork", "access denied", "empty results", depending on what settings I used, ... even though the same code worked from command line on the server itself.

$retstr = exec('nslookup -type=mx myhost.com', $retarr);

Instead of nslookup I believe this would apply to most programs from the \system32\ directory.

I had to learn that the following finally worked:
$retstr = exec('c:\php\safedir\nslookup -type=mx myhost.com', $retarr);

... but only under the listed preconditions:
1: nslookup.exe is placed (copied) in the directory \php\safedir\
2: the directory \php\safedir\ is included in the system PATH environement variable
3: the file cmd.exe is placed in \php\ as listed by other notes above
4: the directory "c:\php\safedir\" is set in the php.ini setting
safe_mode_exec_dir = "c:\php\safedir\"
.. maybe set in php-activescript.ini as well, depending on your system setup.
5: nslookup is referenced by the full path as otherwise the file from \windows\system32\ will be called. This happend to me with empty result due to missing rights!

Hope this helps somebody saving some time and headaches.
[end of quote]

This is just to complicated. Only two things are needed:
1. Specific permissions for the IUSR account for read & execute to the cmd.exe in C:\Windows\System32 directory
2. Specific permissions for the IUSR account for read & execute to the command that's needed (example: nslookup.exe in C:\Widnows\System23 directory)

With just this two conditions the exec works fine

(This is for an IIS server running on a windows platform)

mosesdinakaran at yahoo dot co dot in (17-Mar-2008 02:49)

Hi

     I had  problem with executing   exec(),shell_exec() (PHP is not running on Safe Mode).

     After spending some time I was able to find out that the Problem is  with SELinux enabled in my system.

      You can make sure about this by seeing the log  /var/log/messages
     So I disabled SELinux and now everything works.

     A Temporary  Disable to selinux #> setenforce 0;

     Since I felt this is not a good Idea I still went through some sites and find a solution which I felt good.

     Fedora's SELinux policy has some booleans - let's see what it has for our apache:
# /usr/sbin/getsebool -a | grep httpd
allow_httpd_anon_write -- off
allow_httpd_mod_auth_pam -- off
allow_httpd_sys_script_anon_write -- off
httpd_builtin_scripting -- on
httpd_can_network_connect -- off
httpd_can_network_connect_db -- off
httpd_can_network_relay -- off
httpd_disable_trans -- off
httpd_enable_cgi -- on
httpd_enable_ftp_server -- off
httpd_enable_homedirs -- on
httpd_rotatelogs_disable_trans -- off
httpd_ssi_exec --off
httpd_suexec_disable_trans -- off
httpd_tty_comm -- off
httpd_unified -- on
 
   From the above we can make sure that exec() is not working  because of this httpd_ssi_exec -- off   setting
  So I turned it On.
  
#> setsebool -P httpd_ssi_exec 1;
 
    Now I was able to see the output of shell_exec('ls -al')

Moses

luko post cz (30-Jan-2008 02:15)

to lancelot:

it's simple exec($my_cmd) on windows executes 'cmd /c $my_cmd'
if there are any spaces in $my_cmd you have to put it in doublequotes:
cmd /c "$app $target" will work

further if there are any spaces in $app and $target they also have to be in doublequotes:
cmd /c ""$app" "$target""

therefore enclose your $cmd in two more doublequotes and dont forget to escape slashes \\

$cmd = "\"\"C:\\my path with spaces\\targetapp.exe\" \"C:\\mypathnospaces\\targetfile.xxx\"\"";

and exec($cmd) will work :)
no chdir anymore...

lancelot--du-lac at hotmail dot fr (25-Jan-2008 07:00)

The note is about usage of exec() under windows...

In replay to k dot mitz dot NO_SPAM at att dot NO_SPAM dot net: why not enclose your $cmd in simple quotes instead?

Here is where i'm at right now (also TRYING to use exec() and passthru() on windows XP sp2):

you can do this:

$cmd = '"C:\my path with spaces\targetapp.exe" C:\mypathnospaces\targetfile.xxx';
exec($cmd);

The above works.
________

Or, you can put your script into your directory C:\my path with spaces\, and work with php directly from there:

$cmd = 'targetapp.exe "C:\my other path with spaces\targetfile.xxx"';
exec($cmd);

The above also works, provided of course your script has the correct working directory.
________

But... In your cmd.exe, you can issue:

"C:\my path with spaces\targetapp.exe" "C:\my other path with spaces\targetfile.xxx"

Although the above works perfectly in the cmd, the following php script does NOT work on my system:

$cmd = '"C:\my path with spaces\targetapp.exe" "C:\my other path with spaces\targetfile.xxx"';
exec($cmd,&$content);

Not that it sends an error, it just does nothing, and you get nothing written into your $content array... Not that I do understand why.
________

As far as my few tryings go, the root of the problem lies in the fact that the command to execute has two passages enclosed inside double quotes. Another command which I've had working in the cmd but NOT with php is:

'"C:\my path with spaces\Apache2\bin\Apache.exe" -w -n "Apache2" -k restart'

Maybe someone has a solution; I don't (other than using chdir).

jakster at gmx dot net (05-Jan-2008 12:56)

To fork a process I use the following code

<?php

fclose
(STDOUT);   //Close all output or it WON'T work
fclose(STDIN);
fclose(STDERR);

if(
pcntl_fork()) {
    exit;           
//Return to the caller
}

//Code to run in the background

pcntl_exec("/usr/bin/php",Array($_SERVER['argv'][1]));

?>

I call this script from my website with
<?php
function fork($script){        //run a forked php script relative to the document root. NO OUTPUT IS DISPLAYED!
   
global $config;
   
$cmd = "/usr/bin/php \"" . dirname($_SERVER['SCRIPT_FILENAME']) . "/includes/forked/forkHelper.php\" \"" . $script . "\"";
   
exec($cmd);
}
?>

Odie (14-Dec-2007 08:22)

When I switched our server over from running cgi mode to FastCGI I found that my exec function was no longer working in one area. I found that even when IUSR_X had read access to the appropriate areas, it could not execute the file.

Permissions on files don't seem to matter with FastCGI. You have to move any executables and files that they access outside of wwwroot. My problem was that I was running an executable that was reading a file in my web directory.

Max_R (26-Sep-2007 03:21)

Just a small notice, when running PHP under MS Windows
some external command-line utilities (like 'ffmpeg.exe') output all info to stderr instead of stdout. In my case it caused apache error.log overfilling with lots of unsolicited records.
It has just taken some time for me to make the utility silent:
Simply add
">NUL 2>&1"
to the very end of your command line. It makes cmd.exe to redirect stderr to stdout and then, in one's turn, redirect stdout to NULL.

Hope this tip will save some time when dealing with such utilities.

steve dot robinson at frogneck dot com (21-May-2007 03:46)

If you are trying to use exec to run a cgi and output the contents via a php file, headers need to be seperated from the content and output seperately.

Also, at least in the case of the cgi I was attempting to execute, line feeds were missing, and some javascript didn't work as a result, so you may have to add line feeds back into the resulting output.  Here is the code I used to output my cgi properly...

<?PHP
putenv
('REQUEST_METHOD=GET');

// to get your parameters passed into the cgi..
putenv('QUERY_STRING=' . $_SERVER['QUERY_STRING']);

//you will know you are past the header when you
//hit a blank line ('')
$inheader=true;
foreach(
$output as $val){
        if(
$val=='')
               
$inheader=false
        if(
$inheader)
               
header($val);
        else
                echo(
$val . "\n"); // output contents
}
?>

php at fendy dot org (03-May-2007 01:51)

Scheduling WinXP tasks with schtasks.exe and using PHP to execute the command, may sometime fail to work.

This is because, Apache does not have the privilege to access some of the System Files when placing the scheduling. The way I'd do: is by creating a normal user account and assign Apache service to logon as that account.

Open the 'services.msc' in the 'Run' window, look for Apache in the listing, right click and get to 'Properties'. Click at the second tab 'Log On' and fill in the 'This account' fields.

Of course, Apache needs to be installed as Service during its first setup.

Hope this helps anyone.

Fendy Ahmad

pawel_do_not at spam_me_lenart dot pl (24-Apr-2007 12:51)

You must remember that if you're running your own compiled C programs and you don't get any output you probably do it like this:

printf("some text");

shell_exec and similiar functions won't display it, you must do your output as follows:

fprintf(stdout, "some text");

and then shell_exec and so will correctly read stdout (spent an hour wondering why my program isn't working when connected with PHP :)

(29-Jan-2007 04:16)

it seams to me the best way would be to copy cmd.exe to a secure directory somewhere under your web directory, add it to the windows path and then call it from there using system commands. It seams as though that would get arround any security holes

Rafael Palacios (29-Jan-2007 02:52)

Here is a way to collect all the output of a command (standard output and standard error):

$command="your command goes here";
$output=shell_exec($command." 2>&1");  //system call
print "<pre>$output</pre>\n";          //show output

Matt-kun (08-Dec-2006 06:50)

Just a simple note to help people get rid of some headaches.

when using:
<?
       $filepath = "the path of the file";
       exec("program -command $filepath ");
       fopen($filepath,rw);
?>

Make sure you use  "\ " ( \ space) for the Linux\unix path and just " " space for fopen. These are both very basic things that you wouldn't normally think to cause a problem, but when you try to pass slashes to fopen it either breaks or even better works incorrectly =D. And vise versa for any programs that use "\ " for spaces in file paths/names.

I ran into this problem when using <? exec(" cp $path "); ?> and <? fopen("$path"); ?>. To fix it I used str_replace(" ","\ ",$path); and str_replace("\ ", " ",$path);

*Note this is alot of sudo code, and there are faster more effecient ways of doing the same operations, but I thought this might help those who were going crazy over filepaths =D.

PHP at jyoppDot-com (04-Oct-2006 06:38)

Here's a function to launch a low-priority background process very simply, returning control to the foreground.  I found that a lot of the other approaches shown here allow the script to continue running, and the page is served up to the user, but the PHP thread that launched the process cannot be re-used until the process has completed.  Stdout and stderr need to be redirected to /dev/null to prevent this.

This is for BSD/*NIX servers only (tested on Fedora Core and OSX).  Win32 approaches would be necessarily very different.  Omit 'nice' if you don't want to run the process with lowered priority.

<?php
function fork($shellCmd) {
   
exec("nice $shellCmd > /dev/null 2>&1 &");
}
?>

t dot kramer at NOSPAM dot safetbus dot de (21-Sep-2006 10:21)

Trying to us the following code failed badly with various results: like "unable to fork", "access denied", "empty results", depending on what settings I used, ... even though the same code worked from command line on the server itself.

$retstr = exec('nslookup -type=mx myhost.com', $retarr);

Instead of nslookup I believe this would apply to most programs from the \system32\ directory.

I had to learn that the following finally worked:
$retstr = exec('c:\php\safedir\nslookup -type=mx myhost.com', $retarr);

... but only under the listed preconditions:
1: nslookup.exe is placed (copied) in the directory \php\safedir\
2: the directory \php\safedir\ is included in the system PATH environement variable
3: the file cmd.exe is placed in \php\ as listed by other notes above
4: the directory "c:\php\safedir\" is set in the php.ini setting
safe_mode_exec_dir = "c:\php\safedir\"
.. maybe set in php-activescript.ini as well, depending on your system setup.
5: nslookup is referenced by the full path as otherwise the file from \windows\system32\ will be called. This happend to me with empty result due to missing rights!

Hope this helps somebody saving some time and headaches.
Thomas

Arjan van Bentem (12-Jul-2006 08:38)

When using Red Hat Fedora, beware of Security Enhanced Linux, SELinux.

Quoted from Red Hat: "The security goal is to make sure that Apache HTTP is only reading the static Web content, and not doing anything else such as writing to the content, connecting to database sockets, reading user home directories, etc."

These limitations include, among many other things, using exec to run external applications that happen to use sockets (or maybe access some files) such as HylaFAX "faxstat" as invoked from nweb2fax recvq.php and sendq.php.

For debugging, one could try running such commands using PHP Shell (see http://mgeisler.net/php-shell/) which might fail while execution from the real command line (as Unix user apache or httpd) yields no problem whatsoever.

See /var/log/messages for any denials due to the SELinux policy. To disable it:

- System, Administration, Security Level and Firewall
- open the SELinux tab
- click the Transition tree
- check Disable SELinux protection for Apache HTTP
- execute /etc/init.d/httpd restart

See also http://fedora.redhat.com/docs/selinux-faq/ and http://php.net/results.php?q=selinux&p=wholesite

judas dot iscariote at gmail dot com (01-Jun-2006 08:19)

This functions are generally considered harmful,without proper input validation procedures are as worse as eval().

If what you are trying to do is possible using other mechanism, ** do it that way, even if it takes more time **.

Do not cry or blame PHP if your server gets "owned" due to a missing escapeshell*** in your code , you have been warned.

manokan at manokan dot net (18-Apr-2006 06:09)

Lets make it even clearer, about the "unable to fork" error in exec().

By default Windows XP sets all permissions for cmd.exe for the temporary internet user account (IUSER-[computername]) to DENY. That overrides everthing.

You must modify the security on cmd.exe to give the IUSER-computername account at least read & execute and remove the DENY.

not at any dot com (23-Feb-2006 08:16)

at LAST!  tenacity pays off, days trying every little thing!

I didn't want batch files.  I'm trying to use PHP to get rid of batch files.

I didn't want to call a file to parse the parameters to call a shell to call a file.  I've got "systems" right now with batches tiered three and five deep.

I just wanted to run stuff.

CALL, tested on WinXP, will be testing on more OSes right away, in PHP4 and 5, with exec, system, works with popen, and passthru.

here is my lame sample function.

//  CreateZip
    function createzip ($target, $archive)
    {   $ziputil = "call \"c:\\Program Files\\7-zip\\7z.exe\"";
        $archived = escapeshellarg($archive);
        $targeted = escapeshellarg($target);

        $shellcommand= $ziputil." a -tzip ".$archived." ".$targeted."\n";

  // all of the below are working in Win XP Pro
    passthru ($shellcommand);
    exec ($shellcommand);
    system ($shellcommand);
    shell_exec ($shellcommand);
    $proc= popen ($shellcommand, "r"); //$proc contains output

LONG PATH NAMES WITH SPACES IN THEM ON WINDOWS!

all in a big long concatenated command line with multiple quoted-filename parameters

shell scripting bliss!

mtupker at yahoo dot com (24-Jan-2006 08:21)

I got the system() function to execute commands, but the command I want to execute is the massive dsadd command below.

for /F "eol=; tokens=1,2,3,4,5,6,7,8 delims=," %a in (output.txt) do dsadd user "cn=%b %a,ou=Testing,dc=mtmercy,dc=edu" -samid %a%b -fn %a -ln %b -display "%b, %a" -pwd %d -email %a%b@mtmercy.edu -disabled no -empid %c -desc Student

I've had limited success creating users in MS active directory 2003 so I decided to try dsadd. The problem is the quotes that are in the command itself as well as the % signs. The quotes are interpreted by php and the % signs are interpreted wrong in batch files. Any thought on this would be appricated.

dragomirov at tiscali dot it (23-Jan-2006 11:52)

IT's great Anthony, your function works even under a *nix machine. You only need to modify like that
<?php
function callTool ($path,$file)
{
  
chdir($path); $call = $path.$file;
  
pclose(popen($call.' &', 'r'));
}
?>
The command goes in bakground and the script is still running.

Thanx a lot!

Anthony Catel (paraboul at gmail dot com (09-Jan-2006 03:40)

<?php
   
function system_o($cmd)
    {
       
exec("$cmd", $f);
        foreach(
$f as $output) {
           
$output = htmlentities($output);
           
$ret .= "$output\n";
        }
        return
$ret;
    }
?>

You can parse the output

afineman at computer dot org (15-Dec-2005 08:09)

In response to the comment by 'daniel dot hopp at godot dot de' regarding the use of start-stop-daemon to exec a process within a session:

start-stop-daemon will probably not be a good choice.  start-stop-daemon with the --start option will not start the daemon if any matching process is found.  In other words, if you are using it to start a process running 'myscript', and there is another instance of 'myscript' found in the process table, start-stop-daemon will *not* start another process and will return 1, unless --oknodo is set.

Check out a man page for start-stop-daemon.

rymfaxe (at) hotmail (dot) com (04-Dec-2005 04:50)

Starting batch or exe files (from a local Apache server 1.3.24 with PHP 4.3.1) to be executed within WinXP SP2 and having the program running in background - which certainly is tricky.

I figured this peace of code makes it very possible.
Instead of using exec() I prefered popen() adding the parameter /b which makes it run in the background, thus having PHP script continued to be interpreted contrary to be hanging in the process.
 
<?php
function callTool ($path,$file) {
   
chdir($path); $call = $path.$file;
   
pclose(popen('start /b '.$call.'', 'r'));
}

// -- Call tool1 -----
$location = "c:\path\to\desired\folder";
$filename = "\tool1.exe";
callTool($location,$filename);

// -- Call tool2 -----
$location = "c:\path\to\desired\folder";
$filename = "\tool2.bat";
callTool($location,$filename);

// -- Call tool3 -----
$location = "c:\path\to\desired\folder";
$filename = "\tool3.com";
callTool($location,$filename);
?>

This example assumes that the Apache properties locally has bin told to 'allowe this server to interact with desktop' which on WinXP is to be set under Start > Run > services.msc - next, right click "Apache...", select properties and click on the 'login tab'.

Hope someone to benefit out of this stuff.

valqk at lozenetz dot org (25-Nov-2005 12:05)

If you are chrooting php into enviornment that doesn't have /bin/sh you can't execute any command! Even when you call
mail() and it calls sendmail ... well actually sendmail NEVER gets called because /bin/sh is not in the chroot!
SO in conclusion: YOU MUST HAVE /bin/sh TO EXECUTE SOMETHING!!!
VERY IMPORTNAT!
I've lost few days while find this, hope it helps someone!!!

ripat at lumadis dot be (08-Nov-2005 07:54)

About the background functions below, many hosting service disable the exec or passthru functions.

It is still possible to run a script in background by sending a 200 OK header to the clients browser.

Here is how:

<?php

header
("HTTP/1.1 200 OK");
header("Content-Length: 0");
flush();

// code to be run in background here
// and, of course, no output to the clients browser from here.
?>

And if you need to output something to the clients browser before executing the code in the background:

<?php

header
("HTTP/1.1 200 OK");

$text = 'Your html code here';

// IE hack to get the flush to work. Not needed for FF.
$text = strlen($text) > 256 ? $text : str_pad($text, 256);

ob_start();
  echo
$text;
 
$size = ob_get_length();
 
header("Content-Length: $size");
ob_end_flush();
flush();

// code to be run in background starts here.
// And, of course, no output to the clients browser from here.

?>

manokan at manokan dot net (27-Oct-2005 11:01)

Even more on the "unable to fork" error.

It was driving me crazy! I did check that the permissions were set for the IUSR_[server] account to read & exec cmd.exe. Yet still it failed. It wasn't until I got desperate that I happened to look in "special permissions." (Click the advanced tab underneath the regular list of permissions.) There I found that  somehow the IUSR_[server] account had a special permission DENYING access! Of course deny overrides everything else.

This was a newly set up install of XP pro. It must be the default of something (I'd guess IIS) to set this deny permission just to make our lives miserable.

senorchamo at netzero dot net (20-Oct-2005 12:34)

I've been trying to get a process to run in the background while the rest of the script which called the process continues to run.  The easiest way I found to do this in Linux is by executing the following:

The script to run in the background is called $ProgExec

<?php
...script before...
exec("$ProgExec > /dev/null &");
...
script after
?>

The "> /dev/null" makes all output go into a blackhole.
The "&" makes the process run in the background.

Works like a charm for me.  Thought I'd spread the good news. :)

abhilashp at clariontechnologies dot co dot in (29-Sep-2005 05:58)

I faced the problem of putting a shell script as a backgroud process using both exec() and system() commands on a FreeBSD Server with PHP 4.4.0

I tried all the above mentioned methods but couldnt succeded, then i coded this script and am sure will work on all Linux flavors... See if ot helps you all guys...

I Put my Shell Script in the variable $ExecCommand

and used the proc_close and proc_open functions the code is as follows :

proc_close (proc_open ($ExecCommand,array(),$somefun));

After the above line the rest of the usual  php script continues....

Hope it helps you guys..

Helpful Harry (23-Sep-2005 03:15)

To tell you the truth, I don't know how... or why... this works, but I am able to spawn background win32 processes whereby the PHP interpreter does not wait on the called program to finish...

<?php
pclose
(popen("start /b <program> <args>", 'r'));
?>

I don't know wheither pclose is neccesary or not, but I can only guess that it frees up memory.

an example would be

<?php
pclose
(popen("start /b C:\php\cli\php script.php', 'r'));
// rest of the program
?>

where script.php would execute independantly of the parent script..

hope this helps,
Harry

php at haz dot dk (17-Sep-2005 07:41)

Problems with PHP+APACHE+SIGALRM?

Note: Apache uses SIGALRM to some of it's internal timer functions, which means that all external programs that you might want to run that relys on SIGALRM won't run as expected!

This includes the popular programs, ping, arping and probably a lot more.

drenintell (17-Jul-2005 02:42)

For those having a problem executing an exe on Windows, try changing the current working directory to the executable's directory.
<?php
chdir
($executable_directory_path);

// Once you have changed the directory try calling exec, system... etc.
exec($cmd);
?>

Regards,
  drenintell

Helpful Harry (15-Jul-2005 04:48)

I figure this is as good a place as any to describe how to use  php with cron

there are two methods, executing directly or using wget...

1) to execute directly

create a php script to tell you where the php interpreter is located:

type 'which php' or just run this simple script

<?php
passthru
('which php');
?>

that should return /usr/local/bin/php or something similiar.  This is the path to your php interpreter.

now for your cron entry:

0 0 * * * /usr/local/bin/php /path/to/phpscript/script.php

make sure this script is out of the document tree.

2) execute with wget

for your cron entry:
0 0 * * * wget http://path.to/phpscript/script.php

now this script has to reside in the document tree so allow for this check...

<?php
if ($_SERVER['SERVER_ADDR'] == $_SERVER['REMOTE_ADDR'])
   
//execute script here
else
    die(
'Invalid Access.');
?>

(and perhaps a get "?auth=password" check)

php at breakpirates dot com (28-Jun-2005 01:36)

The whole Windows 2003 server solution with changing permissions on cmd.exe for IUSR doesn't seem ideal IMO. It does open up serious security issues. Taking a copy and placing it in the PHP folder could potentially expose your server to exactly the same risks where a HTTP exploit could be used to access cmd.exe and then basically have the rights to run anything on your server...not good. Having your PHP install on a different drive may help things but it's still not ideal.

The only other thing I could think of is using a replacement cmd.exe in c:\php with tight restrictions on where it can run things from, for example c:\myexecutables . I doubt PHP does any checks on the cmd.exe within its own folder so switching this for a different one would probably be fine.

(20-Jun-2005 06:40)

Comment to code posted by info at skyco dot de:
On Apache 1 and PHP 5, the code didn't work as expected?

Something like this worked better:
<?
// put this in the calling file:
function callWebsite($strHost, $strPath="/") {
               
// try opening the connection
$fFile = @fsockopen($strHost, 80, $intError, $strError);
   
    if ($fFile) {
        // make a simple GET-command
        $strCommand = "GET ".$strPath."\r\n";       
        // send the command
        fwrite($fFile, $strCommand);           
        // close connection
        fclose($fFile);                 
        return true;
    } else {
        // report error
        echo "Could not connect to ".$strHost. " (".$intError.") ".$strError.".");           
        return false;
    }
}   

// call like you were outside yourself... (127.0.0.1=your own computer)
callWebsite("127.0.0.1", "/yourpath/yourfile.php?example_variable=24");
?>

<?
// and do this in the file that should run on it's own:
ignore_user_abort(true);
// if it should run longer than timeout, do this:
set_time_limit(0);
// put your code below here
?>

Your last file could be tested in telnet too. To do this, enter this using the command prompt:
telnet www.yourhost.com 80<enter>
GET /thepathtoyourfile/file.php?hello=yes<enter>

xmb at skilled dot ch (01-Apr-2005 03:25)

ronny posted:
    $uptime = exec("expr $(awk '{print $1}' < /proc/uptime | awk -F. '{print $1}' ) / 86400");

-> real awk:
    $update = exec(" awk '{ print $1 / 86400 }' </proc/uptime ")
    $update = exec(" awk '{ printf "%02.2g", $1 / 86400 }' </proc/uptime ")

info at skyco dot de (19-Mar-2005 04:13)

I noticed that a couple of contributers planned on having threads running in background while operation continues.

If you have a script that will function for itself there is an easy solution: call it through fsockopen (request to webserver) and do not wait for the answer. To make sure process is called correctly on all operating systems I had to add an fgets but break after first couple of bytes.

example main script

<?php
   
echo "This is main thread, returning immediately";
   
$fp = fsockopen( <addyourcalltoscript> );
    
//fputs($fp, <yourvariablestosend>);
    
while(!feof($fp)){
         
fgets($fp, 128);
          break;
     }
   
fclose($fp);
?>

example child script

<?php
   
// make sure script runs without stopping //
    
set_time_limit(0); 
    
ignore_user_abort(true);
   
// your code
?>

sven at nospam grovestreet dot net (09-Mar-2005 06:42)

Hey,
I had this problem: I wanted to kick off a PHP script that would possibly run for a while. This is on a web GUI, so there is the problem of a) the user stopping the browser, and b) reloading or revisiting the page.

Since this will live on a hosted box over which I have little control, am unsure of whether CLI or the CGI is installed, and didn't want to add Perl to the mix, I came up with this hack.

<?
$logfile = dirname(__FILE__)."/mail.log";

if (file_exists ( $logfile )) {
    if ( (time() - filectime( $logfile ))<10 ){
    // file exists and was changed less than 10 secs ago
        header("Refresh: 2; url=''");
        print "process running....<br><br>\n\n";
   // the below just inverts the log and prints it
        $logcontents = file($logfile);
        foreach (array_reverse($logcontents) as $line) {
            print $line."<br>\n";
        }
    } else {
   // file exists but has not been changed in 10 secs
        print "process finished!<br><br>\n\n";
        $logcontents = file($logfile);
        foreach ($logcontents as $line) {
            print $line."<br>\n";
        }
   // dump the file so I can run the proces again
        unlink ($logfile);
    }
} else {
echo "\n\n";
echo "startig process...<br><br>\n\n";
ob_flush();
flush();

$log = fopen($logfile, "a") or die("error opening log\n");
// don't kill this process no matter how long it runs
set_time_limit(0); 
// keep running even if the user stops the browser
ignore_user_abort(true);
// dummy stuff. put the real work here
for ($i=0; $i<20; $i++) {
    $thisline = "iteration $i";
     fputs($log, $thisline."\n");
    print $thisline."<br>\n";
    ob_flush();
    flush();
    sleep(1);
}
fclose ($log);
}
?>

This yields:

1st hit:
  startig process...

   iteration 0
   iteration 1
   iteration 2
.... adding lines as long as the request is not stopped

on subsequent hits

   process running....

   iteration 2
   iteration 1
   iteration 0

and refreshing every 2 secs

and finally:

   process finished!

   iteration 0
   iteration 1
   iteration 2
   iteration 3
   iteration 4
 .... rest of log file

Seems to be pretty stable... - any comments in email please.

sven

mr_potty_noodle at hotmail dot com (16-Feb-2005 03:11)

You may have issues with RUNAS or CPAU using the exec function on win2k server (Fully patched as of 2005 in my case), Windows 2k3, Windows XP SP2 may require special consideration if your services use the local system account.

It seems Microsoft changed the CreateProcessWithLogonW API call in the backend that makes it incompatible with localsystem account.

This means that some applications WILL NOT RUN, the process will not be created, it will be denied. At first i thought this was a permissions issue, however after looking into it, its clear that it is not.

A solution to the issue is to modify the system service that your web server uses to run from the local system account. In my case this was not an option as the WWW (iss) services on win2k server are integrated with other services which will cause problems.

Microsoft have come up with a solution in a vbs script. The following is a quote from a Microsoft support e-mail, it was aimed at winXP sp2. I have no idea if it will resolve the issue on win2k / win2k3 machines at the moment.

"Thank You for contacting Microsoft Support in regards to your question with CreateProcessWithLogonW() not working on XP SP2 from the localsystem account.

In our internal support database Ive found the following answer from an American colleague to exactly the same question:

The code for CreateProcessWithLogonW() was changed in SP2 to have the same behavior as Windows Server 2003. One of the things CreateProcessWithLogonW() will do ensure that the child process has access to the windowstation/desktop if lpDesktop member is specified to NULL or empty string. On Windows XP SP1, the operating system
would grant explicit access to the child process for the windowstation/desktop of the parent process. On Windows XP SP2, the API determines the logon SID of the parent process and adds this to the token of the child process so the API doesn't need to modify the DACLs for the windowstation/desktop. This will not work with the localsystem account since it doesn't have a Logon SID.

If you would like to launch the process interactively (ie winsta0\default) with XP SP2, you'll need to do the following:

1. set lpDesktop to "winsta0\\default".
2. grant the user in the CreateProcessWithLogonW() call access to
"winsta0\\default". The following KB article has sample code on modifying the security using VB:

316440 How To Use Low-Level Access Control APIs from Visual Basic
http://kb/article.asp?id=Q316440

The function you need to look at is: UpdatePermissionsOfUserObject()

To determine what type of permissions should be granted. Please take a look at the following article:

165194 INFO: CreateProcessAsUser() Windowstations and Desktops
http://kb/article.asp?id=Q165194

#define WINSTA_ALL (WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS |
WINSTA_CREATEDESKTOP | WINSTA_ENUMDESKTOPS |
WINSTA_ENUMERATE | WINSTA_EXITWINDOWS |
WINSTA_READATTRIBUTES | WINSTA_READSCREEN |
WINSTA_WRITEATTRIBUTES | DELETE |
READ_CONTROL | WRITE_DAC |
WRITE_OWNER)

#define DESKTOP_ALL (DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD |
DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP |
DESKTOP_WRITEOBJECTS | DELETE |
READ_CONTROL | WRITE_DAC |
WRITE_OWNER)

The values for the above defines are in winuser.h.

I have tested this from localsystem account with XPSP2 and it works using the code from the above article, 165194.

Shaun (26-Jan-2005 09:53)

Trying to use 'exec' to run a helper executable on Win2K3 (and I'm told this also happens on Win2K) from a PHP script running on IIS, failed to invoke the executable.

Running the same PHP script on Win2K using Apache to serve the page calling the same helper executable worked.

Solution: On Win2K3 (and probably Win2K), give the IIS IUSR_xxxxx guest user Read & Execute permissions to Cmd.exe found in the System32 folder under Windows root directory.

This one had me confused longer than it should have! Hope this saves others from the same fate!

louis dot j dot scoras at gmail dot com (23-Jan-2005 06:43)

To start a process to run in the background, you can use the same technique you would in writing a daemon.  Fork a new child while exiting in the parent, then set that as the process leader.  Finally, fork once more, and run whatever the command is:

<?php

if(pcntl_fork()) {
    exit;
}
posix_setsid();
if(
pcntl_fork()) {
    exit;
}
pcntl_exec('/usr/bin/ogg123', array('-q', $this->findPath()));

?>

Of course, this would be the last thing that your script does, because the parent exits when you spawn the new process.  You could do more fancy things, but this works as a good starting point.

margus at kohv dot com (18-Jan-2005 11:10)

Well, I had this issue when I wanted to start an indefinitely running PERL script from PHP. Somehow I could not make the PHP script to return after executing the PERL script. So finally I came to following solution:

PHP file
-------------------
<?php
exec
("perl /home/chatserver.pl > /dev/null");
?>
-------------------
This script will be run over HTTP.

PERL file
-------------------
#!/usr/bin/perl
fork and exit 1;

# normal code here...
-------------------

Hopefully it helps someone :)
Margus

sean @T keenio - dot - com (09-Jan-2005 07:35)

Just to clarify, background shell exec()ing is not the same as a fork() under Unix. It would be useful if PHP supported fork() like Perl and other scripting languages do. Simply exec()'ing a process and sending it to the background is not the same.

A true fork() copies the current environment and duplicates the running script so that there are two instances, one child, one parent. The child and parent can determine which copy they are and then take separate courses of execution paths based on that. Often one copy will perform some "busy work" while the other returns to normal execution. But no matter what, both functions live within the same code rather than a separate, external program. exec()ing can not do this for you.

sam at freepeers dot com (02-Oct-2004 01:51)

To clarify even more about what has been said in the last few posts:

"exec", "backticks", "system" and so on will fail on Windows 2003 by default. 

You must modify the security on cmd.exe to give the user account IUSR-computername the necessary permissions which are at least read & execute.

leaetherstrip at inbox dot ru (08-Jul-2004 10:23)

Note on XP users: XP-Home edition does not allow to set rights directly on files and folders. You should use cacls command-line utility to do this.

For example:

cacls c:\windows\system32\cmd.exe /E /G IUSR_ADMIN2003:F

gives IIS user full access to cmd.exe (potential security hole!), so PHP can fork and execute external programs.

dotpointer (15-May-2004 02:02)

Win32 / PHP 4.3.6...

If you plan to start programs on the server that show message boxes (things that require OK from the server-side), or remain (like notepad.exe), and the exec-command seems to go into an deadly loop, then MAYBE your program has started, but you can't see it. Because the web server runs as an system process, and it isn't allowed to interact with the desktop.

To solve a part of the problem (to see the programs you execute), in the control panel in Windows, goto Administration->Services, right-click the server-service, goto Properties and on the second tab (login?) and check the box about allowing the service to interact with the desktop. Click OK. Restart the webserver, and MAKE SURE it is restarted for real (i.e. look in the task manager so the service goes _down_), otherwise the desktop-option won't take.

Next phase would be to stop PHP from waiting for the processes to complete (this is what makes PHP "loop"). I solved this by creating a small Delphi-application that took the path to the file I wanted to execute and executed it by the WinExec API, which just starts the childprogram but doesn't wait for it to complete = the program completes and the PHP.exe completes the script. Problem solved!

Delphi-snippet:
WinExec(PChar(<CMD>),SW_SHOW); // replace <CMD> with the program path.

vosechu at roman-fleuve dot com (26-Mar-2004 02:35)

Quick chart that helped me immensely (Best (+) / worst (o) of each command) and when to use each (w).

| exec(string cmd, [array cmd_output, [int cmd_exit_status]])
+- Second (optional) argument holds an array containing the output line by line.
+- Third (optional) argument holds the exit status of the executed program.
w- When the output of a command is only important to the program.

| system(string cmd, [int cmd_exit_status])
+- Echos ascii output straight to browser (only stdout; stderr must be redirected by putting 2>&1 after the command to see errors).
+- Second (optional) argument holds the exit status of the executed program.
w- When the output of a command is important to the user.

The more I go over my notes the less I care for the last two functions.
Shell_exec() = exec() + implode() - exit codes.
And while neccessary to be able to pipe binary through php, most people will never use passthru

| passthru(string cmd, [int cmd_exit_status])
+- Echos binary output directly to browser window.
+- Second (optional) argument holds the exit status of the executed program.
w- When the output of a command is binary.

| shell_exec(string cmd) <-- minimal features to maintain sync with normal use of backticks.
+- Returns full output as a string instead of directly to the browser (only stdout; stderr must be redirected).
o- No way to check exit status (Should use system() with both stderr and stdout redirected to /dev/null to get the exit code).

ferchland at computer-kontor dot de (22-Jan-2004 04:24)

I tried to fork a GUI-process on WinNT, Apache 2, PHP4.
I saw the new process (notepad.exe) in the Task-Managers process-list, but no Notepad-Window anywhere!
After activating the checkbox "Interactive with Desktop allowed" in Services->Apache everthing works with a simple command
exec("start c:\\winnt\\notepad.exe");

jeremy at ntb dot co dot nz (28-Sep-2003 05:18)

If an error occurs in the code you're trying to exec(), it can be challenging to figure out what's going wrong, since php echoes back the stdout stream rather than the stderr stream where all the useful error reporting's done. The solution is to add the code "2>&1" to the end of your shell command, which redirects stderr to stdout, which you can then easily print using something like print `shellcommand 2>&1`.

dens at dodgeball dot com (18-Sep-2003 11:41)

I was stuck for about an hour and a half last night trying to get Perl to pass back values to my PHP script after running an exec() command.

Since my Perl script was using STDOUT (after being passed command lines variables), I expected the variables used in my .pl script to be accessible in PHP, no questions asked.

Of course, this wasn't working and I finally figured it out with the help of a friend (Shawn = superstar):  you need to echo the exec() command in order to get the values back into PHP. 

Since I was returning multiple values, I added pipe delimiters in my .pl script and then used PHP to parse the string to retreive my data into different variables.

Check it out:

#hit the .pl script with the data
$mydata = exec("script.pl 'command line args' ");
exec("exit(0)");
   
#parse the value that comes back from the .pl script
list($strA, $strB) = split ('[|]', $mydata);
echo "valueA: " . $strA."<br>";
echo "valueB: " . $strB."<br>";

Woo-hoo!
/d

software at yvanrodrigues dot com (29-Jul-2003 10:55)

MORE ON THE UNABLE TO FORK MESSAGES IN WINDOWS:

I would like to confirm that this issue relates to permissions on %SYSTEMROOT%\SYSTEM32. The user (usually the anonymous login) must have execute permissions on cmd.exe

This is unlike most other programming languages. For example, in C the spawn and exec functions do not try to open a shell, they create the new process directly. PHP creates the new process via cmd.exe (or command.com) much like the C system() function. This is good for those of you who are trying to run batch files but this is very ineffecient for running other .exe files.

I feel uneasy about lifting permissions in my system32 directory but you can get around this by copying cmd.exe to your PHP directory. Windows will look there first and if it is not there it will check the path. Note: I mean the directory where php.exe is, not your script directory.

I have confirmed this by running filemon.exe while trying to execute a script, and you can see it trying to start the cmd.exe process.

kristof_jebens at hotmail dot com (23-Jun-2003 10:23)

For WIN2K Server users running Apache 1.3.22 who are unable to run an executable...

exec('c:\\WINNT\\system32\\cmd.exe /c START c:\\file.exe');

this is the only way it worked for me.
Hope that helps

david at mego dot com dot tw (02-Jun-2003 07:48)

Okay here is my two cents for Windows users having "Unable to fork..." errors when executing "shell_exec" or "system" functions.

After numerous hours of testing, I realized that this error is a result of insufficient file permissions.  You will need to locate c:\Windows\system32\cmd.exe and set the permission to at least Read & Executable for Internet Guest user accounts.

Then you should be able to run your script without a problem.  If you still have a problem feel free to contact me by email.

leenoble at spammenot dot ntlworld dot com (09-May-2003 05:37)

Even I typed in the full path to a command none of the shell functions worked at all.

exec("/usr/bin/mygoodcommand");

...just didn't produce results.

The command was world executable and ran fine from the shell. But after playing with the 2>&1 thing I established that "mygoodcommand" needed all of its commands to be path specific too. In my case I was attempting to dump a processed uploaded spreadsheet to a database with the command:

mysql --local-infile -u supportadmin --password='*******' personnel < /Users/leenoble/Sites/Websites/Support/data/updateexec.sql

In order for this to work I had to add:

/usr/local/mysql/bin/ to the mysql command to make it work. I hope this helps stop someone else from tearing their hair out in frustration.

mk at neon1 dot net (20-Apr-2003 05:17)

If you want to execute background processes from PHP that should run indefinitely, be aware of the following nasty behavior (confirmed with PHP 4.3.1 under FreeBSD, but probably under other flavors of UNIX, too):

PHP internally calls setitimer(2) to set the profiling interval timer (ITIMER_PROF) to enforce execution time limits (max_execution_time and max_input_time). These timer values are passed down to all processes executed by PHP. This means that after such a process has consumed [max_execution_time] seconds of CPU time (not wallclock time!), the system will send it a SIGPROF (signal 27). Since most processes don't use this signal and as such don't call signal(3) to catch or ignore it, the default action is executed, which is to terminate the process.

So if you see background processes that were executed from PHP dying at seemingly random times, you may be experiencing this issue. The solution is simply to call set_time_limit(0) just before executing the process.

jpgiot at ifrance.com (26-Jan-2003 02:30)

FOR WINDOWS USERS

i had a look to start command. it's really usefull in fact.

this is how i launch a specific Apache SSL server

<?php
$cmd
= "start /D C:\OpenSA\Apache /B Apache.exe -D SSL";
exec($cmd,$output,$rv);
?>

/D specify the path where application will be launched (equivalent to cd to path)
/B launch application without a console window

so to know the exact parameters of start, you could use

<?php
exec
("start /? > start.txt");
?>

and you will get the help of start in a file named 'start.txt' in the same place you run the script

for WIN 98 you may need to modify a little your command

exec("COMMAND.COM /C START program args >NUL");

(taken from another post)

jfl at eksperten dot dk (10-Jan-2003 06:01)

I made my script work in the background like this:
function start_shell ($cmd) {
    exec('nohup "'.$cmd.'" > /dev/null &');
}

qscripts at lycos dot nl (07-Jan-2003 01:45)

Why not using the "start" utility, provided with every version of Windows to start processes in the background on Windows machines? For example :

exec("start song.mp3");

Which will cause the default handeling application for .MP3 file types to start playing the selected song..

NOtvdgeerSPAM at NOxs4allSPAM dot nl (03-Dec-2002 12:36)

For users of PHP4 & Apache on Windows2000/XP:

If you're trying to execute a command-line application from PHP that has to show a (console) window on your desktop, make sure you enable the option to 'allow service to interact with desktop' in de service properties of Apache. (See your Windows services)

CAUTION: This can lead to security problems if your setting up a publicly available webserver!

k dot mitz dot NO_SPAM at att dot NO_SPAM dot net (15-Sep-2002 10:07)

Newbie note:

The only syntax I found to work for the command portion of an an exec() call on a Win2K devel platform is:

$cmd = "\"path-to-exe\" args file-name";

where 'path-to-exe' has escaped double quotes, args are in the standard format, and 'file-name' has any backslashes escaped.

Example:

$cmd = "\"C:\program files\winzip\wzunzip.exe\" -c C:\\temp\\uploaded_file.zip";

exec($cmd,$output,$rv);

Note that the backslashes are escaped in the uploaded_file name, but not in the path to call the Winzip executable.  Go figure!

kop at meme dot com (12-Aug-2002 10:52)

AFICT, the standard Unix Apache configuration causes an rare problem when running a job in the background. The MaxRequestsPerChild directive causes the child to terminate after 1000 requests, any background processes associated with the child will die with the child unless they are started with the "nohup" command.  Thus, the proper way to start a job in the background is to use:

exec('nohup my-command > /dev/null 2>&1 &')

ramirosuarez at fibertel dot com dot ar (13-Jul-2002 04:58)

For Windows Users:

Keep in mind that a lot of UNABLE TO FORK Errors are the result of insufficient permissions.

CMD.EXE, TEMP Directory (or whatever you specified in php.ini), and all the directories that you use to upload o manipulate your files need to have Write privileges usually user USER.

This will be useful for all the people who use GD Libraries or other programs that manipulate graphic images.

GCMXZPJPDJER at spammotel dot com (30-Jun-2002 08:41)

to execute a script in background from php you don't need to use mikehup or other tools. just do:

`/usr/bin/php -q foobar.php >/dev/null 2>&1 &`;

mat

deschampsbest at noos dot fr (26-Jun-2002 09:05)

I needed to know when a group of background tasks, which had been launched with a call to system, had terminated.
I could not find a function to call, so I created a little function.

It basically loops on the existance of a string in the results returned by the command ps.

All my commands, which run in background have the pid returned by posix_getpid in them.

I am able to launch many background task and effectively wait for them all to finish.

Before this, I would basically would sleep for N seconds.

I apologise if a well known or better method exist.

Thanks,
The New Bee
===============
===============

function wait_until_termination ($id, $duration){
                $i = 0;
                do {
                        $i += 1;

                        $cnt = `ps -auxww |
                                grep $id |
                                grep -v grep |
                                awk '{print $2}' |
                                grep -v $id|
                                wc -l` ;

                        if ($cnt > 0) {
                                sleep ($duration);
                        }

                } while ($cnt != 0);

                echo ("wait_until_termination (PID: $id, SEC: $duration) : LOOPS
:$i<BR>");
        }

rolland dot dudemaine at msg-software dot com (11-Apr-2002 01:45)

In case you ever had to chain from php to another program (e.g. with a cgi php that only gives part of the output, or with php-gtk), here is a little C program that kills his parent (php, for instance), then launches a program given in argument.

chain.c :

#include <unistd.h>

int main(int argc, char**argv) {
  /* kill the parent */
  kill(getppid(), 15);
  argv++;
  /* then launch the new program */
  return execvp(argv[0], argv);
}
(compile with gcc -O3 -o chain chain.c)
then in php, use
<?
exec('chain sh -c echo test');
?>

vi_pa at hotmail dot com (05-Apr-2002 12:17)

For those who want to execute a .php script to run in the background, from a
.php web page...

exec("php script.php parameters 2>dev/null >&- <&- >/dev/null &");

Where...
- php is the path to your php script executer (php has to be specifically complied to be to do this)
- script.php is the script
- parameters are none or more parameters
- 2>dev/null redirects the stderr to a file
- <&- switches off the stdin
- >&- switches off the stdout
- >dev/null redirects all other output to the file dev/null
- & direct script to run in background

This is the only way I managed to get it working (Linux & Apache, php 4x enviroment) . What is also great is that the process is forked so even though a user may close the browser that initiated the exec(), the process will still run to completion. And the dev/null file can be turned into a log.

What I found odd is that the script I was execing would run fine in the bg when executed from the command line, but not from the browser until I closed the stdin and stdout.

daniel dot hopp at godot dot de (01-Feb-2002 07:25)

Re: session_write_close()

To launch a background process within a session, I recommend
to use a start-stop-daemon and ship parameters via ENV.

Example:
----------------------------------------
<?php
 
// Within a session..
 
exec("/some/where/launch.sh 300");
 
// ..finished immediately.
?>

----------------------------------------
#!/bin/bash
# /some/where/launch.sh
T=$1
export T
DAEMON=/some/where/runme.pl
start-stop-daemon --start --quiet --background --exec $DAEMON

----------------------------------------
#!/usr/bin/perl
# /some/where/runme.pl
my $t = $ENV{'T'};
sleep($t);

----------------------------------------

edwin at bit dot nl (23-Jan-2002 11:47)

If you are using sessions and want to start a background process, you might have the following problem:
The first time you call your script everything goes fine, but when you call it again and the process is STILL running, your script seems to "freeze" until you kill the process you started the first time.

You'll have to call session_write_close(); to solve this problem. It has something to do with the fact that only one script/process can operate at once on a session. (others will be lockedout until the script finishes)

I don't know why it somehow seems to be influenced by background processes, but I tried everything and this is the only solution. (i had a perl script that "daemonized" it's self like the example in the perl manuals)

Took me a long time to figure out, thanks ian@virtisp.net! :-)

lk1 at reglos dot de (25-Oct-2001 11:05)

To keep a program running in the background on win98 the command
exec("COMMAND.COM /C START program args >NUL");
works fine for me.

naken at naken dot cc (15-Aug-2001 10:17)

Anyone who still wants a copy of mikehup.c,
I rewrote it:  http://www.naken.cc/mikehup.php
This program will help you run programs in the
background that are called with the php system()
function.