I would like to daemonize a php script (Jobque.php) under PHP 5.3 in Linux
The idea is to call the script like this:
php -f ./application/Model/Jobque.php start
and than the script would do a shell_exec to put itself in the background and do the processing like this
nohup php -f /var/.../application/Model/Jobque.php process 2> /dev/null & echo $!
this last command indeed starts the script in the background and all is fine, however when I issue this command from within the script itself the script waits until the execution stops (never)
this is the function I use to start the script as a daemon - the Windows part works
public function start_daemon() {
if (file_exists ( $this->pidfile ))
die ( 'process is already running - process pidfile already exists -> ' . $this->pidfile );
$cmd = 'php -f ' . __FILE__ . ' process';
if (substr ( php_uname (), 0, 7 ) == "Windows") {
$WshShell = new COM ( "WScript.Shell" );
$oExec = $WshShell->Run ( "$cmd /C dir /S %windir%", 0, false );
exec ( 'TASKLIST /NH /FO "CSV" /FI "imagename eq php.exe" /FI "cputime eq 00:00:00"', $output );
$output = explode ( '","', $output [0] );
$pid = $output [1];
file_put_contents ( $this->pidfile, $pid );
} else {
$execstr = "nohup $cmd 2> /dev/null & echo $!";
//echo $execstr; -- the execstr is right in itself
$PID = shell_exec ( $execstr );
//echo $PID; -- we never get here
file_put_contents ( $this->pidfile, $PID );
}
echo ('JobQue daemon started with pidfile:' . $this->pidfile);
}
what am I doing wrong here, and how to do it right?
Take a look at: http://pear.php.net/package/System_Daemon
I ended up using pcntl_fork for daemonizing the script under Linux, like this:
public function start_daemon($worker) {
if (file_exists ( $this->get_pidfile ( $worker ) ))
die ( 'process is already running - process pidfile already exists -> ' . $this->get_pidfile ( $worker ) . "\n" );
$cmd = 'php -f ' . __FILE__ . ' process';
if ($this->is_win) {
$WshShell = new COM ( "WScript.Shell" );
$oExec = $WshShell->Run ( "$cmd /C dir /S %windir%", 0, false );
exec ( 'TASKLIST /NH /FO "CSV" /FI "imagename eq php.exe" /FI "cputime eq 00:00:00"', $output );
$output = explode ( '","', $output [0] );
$pid = $output [1];
file_put_contents ( $this->get_pidfile ( $worker ), $pid );
echo ('JobQue daemon started with pidfile:' . $this->get_pidfile ( $worker ) . "\n");
} else {
$PID = pcntl_fork ();
if ($PID) {
file_put_contents ( $this->get_pidfile ( $worker ), $PID );
echo ('JobQue daemon started with pidfile:' . $this->get_pidfile ( $worker ) . "\n");
exit (); // kill parent
}
posix_setsid (); // become session leader
chdir ( "/" );
umask ( 0 ); // clear umask
$this->process_jobs (); //start infinite loop
}
}
Take a look at the daemonize command: http://software.clapper.org/daemonize/index.html
Or, "upstart" on ubuntu: http://upstart.ubuntu.com/
Related
I'm having troubles with using proc_get_status after a pcntl_fork in the parent process.
Here's an example with docker and PHP 7.4, but note the PHP version does not matter, the result is the same from at least PHP 7.2 to PHP 8.1.
Setup
Dockerfile
FROM php:7.4
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions pcntl
Run: docker build -t php-pcntl-7.4 .
test.php
<?php
if ($argv[1] ?? false) {
pcntl_async_signals(true);
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
if ($pid === -1) {
throw new \RuntimeException('fork failed');
}
if ($pid === 0) {
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
throw new \Exception('child should be ignored');
}
pcntl_waitpid($pid, $childStatusCode);
}
function runCommand(string $command): void
{
$pipes = [];
$proc = proc_open($command, [['pipe', 'r'],['pipe', 'w'],['pipe', 'w']], $pipes);
do {
if (isset($status)) {
usleep(300000);
}
$status = proc_get_status($proc);
} while ($status['running']);
echo 'Command: ' . $command . PHP_EOL;
echo 'last proc_get_status exitcode: ' . $status['exitcode'] . PHP_EOL;
echo PHP_EOL;
}
echo 'PHP version: ' . PHP_VERSION . PHP_EOL;
runCommand('/bin/echo test');
runCommand('/bin/false');
Test without running a fork before
Run: docker run -it --rm -v $PWD:/workdir -w /workdir php-pcntl-7.4 php test.php
Output:
PHP version: 7.4.28
Command: /bin/echo test
last proc_get_status exitcode: 0
Command: /bin/false
last proc_get_status exitcode: 1
Test with running a fork before
Run: docker run -it --rm -v $PWD:/workdir -w /workdir php-pcntl-7.4 php test.php 1
Output:
PHP version: 7.4.28
Command: /bin/echo test
last proc_get_status exitcode: -1
Command: /bin/false
last proc_get_status exitcode: -1
Does anyone have an idea of why the exitcode is always -1, and if there is a workaround ?
I try this
in var/www/shop/ I have composer.json and lock
putenv('COMPOSER_HOME=' . static::$root); // var/www/shop/
$cmd = '/usr/bin/composer "cd ' . static::$root . 'composer show" 2>&1';
exec($cmd, $output, $return);
$result = print_r($output,true) . ' - ' . $return;
the result is :
Array ( [0] => [1] => [2] => Command "cd var/www/shop/composer show" is not defined. [3] => [4] => ) - 1
If I do inside the directory : var/www/shop/
composer show
I have the good result
thank you.
I think your issue is trying to do the directory change as the composer command, which will not work. You should be able to run multiple commands separating them with &&. Try this:
$cmd = 'cd ' . static::$root . ' && composer show';
Following Command search a file in a directory and zip it,it works well
$command = "cd {$root}/files && mkdir -p {$identifier} && zip -jFS -0 {$root}{$zipname} 2491/'test&.txt'";
exec($command);
But changing files as variable is not allowing shell to execute,the below code does not work
$container_name = "2491";
$files = Array ( '0' => 'test&.txt' ,'1' => 'test5.txt','2' => 'test6.txt');
$files = " " . $container_name . "/'" . implode("' " . $container_name . "/'", $files) . "'";
$files = str_replace('$', '\$', $files);
$command = "cd {$root}/files && mkdir -p {$identifier} && zip -jFS -0 {$root}{$zipname} {$files}";
exec($command);
$root,$identifier,$zipname not causing the problem,its $files What can be the issue?
Update
var_dump for $command before execution:
string(128) "cd /var/www/files && mkdir -p zip--1 && zip -jFS -0 /var/www/files/zip--1/1002_22-06022-06022-_content.zip 2491/'test&.txt'"
which if I execute as
exec("cd /var/www/files && mkdir -p zip--1 && zip -jFS -0 /var/www/files/zip--1/1002_22-06022-06022-_content.zip 2491/'test&.txt'");
runs perfectly
Error Reponse:
zip error: Nothing to do! (/var/www/files/zip--1/1002_22-06022-06022-_content.zip)
I got it fix, issue was with file name having '&' which was chanding to '&', thus breaking the command. To get it I passed through the file name with [htmlspecialchars_decode].1
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
php exec command (or similar) to not wait for result
exec() waiting for a response in PHP
I have a php script that calls and runs a Matlab script. The result of the Matlab script is a .png image, which I would then like to load in php and send to a webpage. The php code I have is:
$matlabExe = '"C:\\Program Files\\MATLAB\\R2012a\\bin\\matlab.exe"';
$mFile = "'C:\\processSatData.m'";
$combine = '"run(' . $mFile . ');"';
$command = $matlabExe . ' -nodisplay -nosplash -nodesktop -r ' . $combine;
passthru($command);
$im = file_get_contents('C:\\habitat.png');
header('Content-type:image/png');
echo $im;
However, it appears that after sending the 'passthru' command, php does not wait for the Matlab script to finish running. Thus, if the image file does not exist before running the php code, then I get an error message.
Is there a way to make it so that the php code waits for the Matlab script to finish running before it attempts to load the image file?
passthru is not the main issue here .. but i guess as soon you have a response from your command the image is not written instantly but by a 3rd process
file_get_contents might also fail in this instance because .. The image might not be written once or in the process of writing which can result to file lock .. in any case you need to be sure you have a valid image before output is sent;
set_time_limit(0);
$timeout = 30; // sec
$output = 'C:\\habitat.png';
$matlabExe = '"C:\\Program Files\\MATLAB\\R2012a\\bin\\matlab.exe"';
$mFile = "'C:\\processSatData.m'";
$combine = '"run(' . $mFile . ');"';
$command = $matlabExe . ' -nodisplay -nosplash -nodesktop -r ' . $combine;
try {
if (! #unlink($output) && is_file($output))
throw new Exception("Unable to remove old file");
passthru($command);
$start = time();
while ( true ) {
// Check if file is readable
if (is_file($output) && is_readable($output)) {
$img = #imagecreatefrompng($output);
// Check if Math Lab is has finished writing to image
if ($img !== false) {
header('Content-type:image/png');
imagepng($img);
break;
}
}
// Check Timeout
if ((time() - $start) > $timeout) {
throw new Exception("Timeout Reached");
break;
}
}
} catch ( Exception $e ) {
echo $e->getMessage();
}
I believe if you change passthru to exec it will work as intended. You can also try this:
$matlabExe = '"C:\\Program Files\\MATLAB\\R2012a\\bin\\matlab.exe"';
$mFile = "'C:\\processSatData.m'";
$combine = '"run(' . $mFile . ');"';
$command = $matlabExe . ' -nodisplay -nosplash -nodesktop -r ' . $combine;
passthru($command);
// once a second, check for the file, up to 10 seconds
for ($i = 0; $i < 10; $i++) {
sleep(1);
if (false !== ($im = #file_get_contents('C:\\habitat.png'))) {
header('Content-type:image/png');
echo $im;
break;
}
}
if (strlen($log) > 0)
{
// Use "WScript.Shell" to run the command with no command prompt window pop up.
$wShell = new COM("WScript.Shell");
$cmd = "cmd /c cscript.exe \"%DIR%\\bin\\eventquery.vbs\" /l \"" . $log . "\" > \"%DIR%\\Temp\\event.log\" 2>&1";
////echo $cmd;
$return = $wShell->Run($cmd, 0, true);
if ($return == 0 || $return ==254)
{
$handle = #fopen(getenv('DIR') . "\\Temp\\event.log", "r");
if ($handle)
{
$linenum = 1;
while (!feof($handle))
{
$buffer = fgets($handle);
// Skip the first three lines
if ($linenum > 3)
{
echo $buffer;
}
$linenum++;
}
fclose($handle);
}
}
else
{
echo "Error running \"" . $cmd . "\" command.";
}
}
It gives an error on windows 7:
ERROR: Unable to include the common module"CmdLib.Wsc"
When I try run it from command line# windows 7, it works fine:
cscript.exe \"%DIR%\\bin\\eventquery.vbs\" /l \"" . $log . "\" > \"%DIR%\\Temp\\event.log\" 2>&1
Try using escapeshellarg:
$cmd = "cmd /c cscript.exe \"%DIR%\\bin\\eventquery.vbs\" /l \"" . escapeshellarg($log) . "\" > \"%DIR%\\Temp\\event.log\" 2>&1";
My guess is that you have shell metacharacters in $log.`
$a = 'all';
file_put_contents('ip.txt',shell_exec($dump = sprintf('ipconfig /%s',$a)));