cron line look's like:
*/1 * * * * /usr/bin/php /path/to/CRON.php > /path/to/log/CRON_LOG.txt 2> /dev/null
CRON.php
<?php
require_once 'config.php';
define('CRON', dirname(dirname(__FILE__)));
$parts = explode("/",__FILE__);
$ThisFile = $parts[count($parts) - 1];
chdir(substr(__FILE__,0,(strlen(__FILE__) - strlen($ThisFile))));
unset($parts);
unset($ThisFile);
$CRON_OUTPUT = "STARTING CRON # ".date("m-d-Y H:i:s")."\r\n";
$CRON_OUTPUT .= CleanLog() . "\r\n";
$CRON_OUTPUT .= "\r\n";
echo $CRON_OUTPUT;
$fh = fopen(''.CRON.'/log/CRON_LOG.txt', 'a');
fwrite($fh, $CRON_OUTPUT);
fclose($fh);
die();
?>
CleanLog function:
global $db;
$resp = '';
$db->query('SQL');
$resp = 'Deleted '.$db->rows_affected.' entries from table';
return $resp;
In file only these two lines showing and function by the time as i can see executed two times:
CRON_LOG.txt
STARTING CRON # 02-26-2012 21:26:01
Deleted 0 entries from table
STARTING CRON # 02-26-2012 21:26:01
Deleted 0 entries from table
What's wrong with it, why it only produce those lines and file doesn't updated (in file only date/time changing, nothing more, it should add more lines and even file size should grow up) ?
You should be using >> for redirection to append to the file, rather than > which overwrites the log each time the script runs.
*/1 * * * * /usr/bin/php /path/to/CRON.php >> /path/to/log/CRON_LOG.txt 2> /dev/null
##---------------------------------------^^^^^^
There really isn't any need to do fopen()/fwrite() inside the script itself, since the cron job is already handling the output redirection.
Related
I'm moving from a webserver with CPanel to one with Plesk. Under Cpanel, it's fairly simple to create and remove cronjobs with php:
<?php
// Create cron
$new_cron = "30 * * * * cd /home/account/public_html/; /usr/local/bin/php -f controller.php ".$argument1.PHP_EOL;
$output = shell_exec('crontab -l');
file_put_contents('/tmp/crontab.txt', $output.$new_cron);
exec('crontab /tmp/crontab.txt');
// Remove cron
$cronjob = "30 * * * * cd /home/account/public_html/; /usr/local/bin/php -f controller.php ".$argument1.PHP_EOL;
$output = shell_exec('crontab -l'); // pull current cron jobs
if (strstr($output, $cronjob)) // found
{
$newcron = str_replace($cronjob,"",$output); // delete it
file_put_contents('../tmp/crontab.txt', $newcron.PHP_EOL); // Save
exec('crontab ../tmp/crontab.txt'); // Send back
}
?>
Under Plesk I have scheduled tasks. How do I use PHP to create and remove those? Or is there another method?
You can schedule task at Home > Subscriptions > example.tld > Scheduled Tasks:
Since Plesk 12.5 there are options of task type:
I've found at this forum that it is possible to edit crontab like this:
a. crontab -l > $tmpfile
b. edit $tmpfile
c. crontab $tmpfile
d. rm $tmpfile
So, I'm trying to implement this solution on php:
include('Net/SSH2.php');
$ssh = new Net_SSH2('myhost');
if (!$ssh->login('user', 'password'))
{
echo'Login Failed';
}
$ssh->exec('crontab -l > /var/www/tmp.txt');
$content=file_get_contents("/var/www/tmp.txt");
$content.='0 0 1 * * /usr/bin/php /var/www/clearPreviousMonth1.php';
$file=fopen("/var/www/tmp.txt", "w");
fputs($file,$content);
fclose($file);
$ssh->exec('crontab /var/www/tmp.txt');
echo $content;
I can see edited content of crontab in my browser and tmp file, but when I use crontab -e it's not changed. What's my mistake?
I had to add new string before the end of file. In other case it's not added to crontab.
changed $content.='0 0 1 * * /usr/bin/php /var/www/clearPreviousMonth1.php';
to $content.="0 0 1 * * /usr/bin/php /var/www/clearPreviousMonth1.php\n";
note that it's neccessary to use " instead of ', because it just will append \n symbols to last string
Basically I have developed a small script that adds a cron job into a file called "crontask" and then I want to execute it so then it becomes a cron job. Here is the script:
<?php
$filename = "../../tmp/crontask.txt";
$output = shell_exec('crontab -l');
$something = file_put_contents($filename, $output.'* * * * * NEW_CRON'.PHP_EOL);
$cngDir = chdir('../../tmp/');
echo exec('crontab ' . getcwd() . '/crontask.txt');
//var_dump($exe);
?>
Everything is ok, the path is the same and if I copy and paste the path that prints out IT will carry out the cronjob but in PHP it won't???
Everything works, apart from the exec function, it doesn't execute it. Any ideas?
In terminal, if I do:
string(25) "crontab /tmp/crontask.txt"
it will execute it.
Try the following things:
Call to the command using the full command path. Sometimes $PATH is not set in the script environment and can't find the command if not.
Setup the working dir of the script using http://php.net/manual/en/function.chdir.php
Use the absolute path to access to the file
I've had a similar issue with cron jobs
I looked at your code and got a couple of ideas.
I used absolute paths
Here is my code:
$myFile = "/home/user/tmp/crontab.txt";
$addcron = "
0 0 * * * cronjob1 " . PHP_EOL .
"0 18 * * 1-5 conjob2 " . PHP_EOL .
"0 9 * * * cronjob3 " . PHP_EOL ;
$output = exec('crontab -l > ' . $myFile );
file_put_contents($myFile,$addcron ,FILE_APPEND);
echo exec('crontab ' . $myFile );
echo "<h3>Cron job successfully added!</h3>";enter code here
basically i wrote the list of previous cronjobs to a file and then appended the file with new cronjobs. Then added the new list of cronjobs with the crontab command.
the linux write to file command ' > ' was what did the trick ;)
Background
I am writing a simple online judge (a code grading system) using PHP and MySQL. It takes submitted codes in C++ and Java, compiles them, and tests them.
This is Apache running PHP 5.2 on an old version of Ubuntu.
What I am currently doing
I have a php program that loops infinitely, calling another php program by
//for(infinity)
exec("php -f grade.php");
//...
every tenth of a second. Let's call the first one looper.php and the second one grade.php. (Checkpoint: grade.php should completely finish running before the "for" loop continues, correct?)
grade.php pulls the earliest submitted code that needs to be graded from the MySQL database, puts that code in a file (test.[cpp/java]), and calls 2 other php programs in succession, named compile.php and test.php, like so:
//...
exec("php -f compile.php");
//...
//for([all tests])
exec("php -f test.php");
//...
(Checkpoint: compile.php should completely finish running before the "for" loop calling test.php even starts, correct?)
compile.php then compiles the program in test.[cpp/java] as a background process. For now, let's assume that it's compiling a Java program and that test.java is located in a subdirectory. I now have
//...
//$dir = "./sub/" or some other subdirectory; this may be an absolute path
$start_time = microtime(true); //to get elapsed compilation time later
exec("javac ".$dir."test.java -d ".$dir." 2> ".$dir
."compileError.txt 1> ".$dir."compileText.txt & echo $!", $out);
//...
in compile.php. It's redirecting the output from javac, so javac should be running as a background process... and it seems like it works. The $out should be grabbing the process id of javac in $out[0].
The real problem
I want to stop compiling if for some reason compiling takes more than 10 seconds, and I want to end compile.php if the program stops compiling before 10 seconds. Since the exec("javac... I called above is a background process (or is it?), I have no way of knowing when it has completed without looking at the process id, which should have been stored in $out earlier. Right after, in compile.php, I do this with a 10 second loop calling exec("ps ax | grep [pid].*javac"); and seeing if the pid still exists:
//...
$pid = (int)$out[0];
$done_compile = false;
while((microtime(true) - $start_time < 10) && !$done_compile) {
usleep(20000); // only sleep 0.02 seconds between checks
unset($grep);
exec("ps ax | grep ".$pid.".*javac", $grep);
$found_process = false;
//loop through the results from grep
while(!$found_process && list(, $proc) = each($grep)) {
$boom = explode(" ", $proc);
$npid = (int)$boom[0];
if($npid == $pid)
$found_process = true;
}
$done_compile = !$found_process;
}
if(!done_compile)
exec("kill -9 ".$pid);
//...
... which doesn't seem to be working. At least some of the time. Often, what happens is test.php starts running before the javac even stops, resulting in test.php not being able to find the main class when it tries to run the java program. I think that the loop is bypassed for some reason, though this may not be the case. At other times, the entire grading system works as intended.
Meanwhile, test.php also uses the same strategy (with the X-second loop and the grep) in running a program in a certain time limit, and it has a similar bug.
I think the bug lies in the grep not finding javac's pid even when javac is still running, resulting in the 10 second loop breaking early. Can you spot an obvious bug? A more discreet bug? Is there a problem with my usage of exec? Is there a problem with $out? Or is something entirely different happening?
Thank you for reading my long question. All help is appreciated.
I just came up with this code that will run a process, and terminate it if it runs longer than $timeout seconds. If it terminates before the timeout, it will have the program output in $output and the exit status in $return_value.
I have tested it and it seems to work well. Hopefully you can adapt it to your needs.
<?php
$command = 'echo Hello; sleep 30'; // the command to execute
$timeout = 5; // terminate process if it goes longer than this time in seconds
$cwd = '/tmp'; // working directory of executing process
$env = null; // environment variables to set, null to use same as PHP
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);
// start the process
$process = proc_open($command, $descriptorspec, $pipes, $cwd, $env);
$startTime = time();
$terminated = false;
$output = '';
if (is_resource($process)) {
// process was started
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt
// loop infinitely until timeout, or process finishes
for(;;) {
usleep(100000); // dont consume too many resources
$stat = proc_get_status($process); // get info on process
if ($stat['running']) { // still running
if (time() - $startTime > $timeout) { // check for timeout
// close descriptors
fclose($pipes[1]);
fclose($pipes[0]);
proc_terminate($process); // terminate process
$return_value = proc_close($process); // get return value
$terminated = true;
break;
}
} else {
// process finished before timeout
$output = stream_get_contents($pipes[1]); // get output of command
// close descriptors
fclose($pipes[1]);
fclose($pipes[0]);
proc_close($process); // close process
$return_value = $stat['exitcode']; // set exit code
break;
}
}
if (!$terminated) {
echo $output;
}
echo "command returned $return_value\n";
if ($terminated) echo "Process was terminated due to long execution\n";
} else {
echo "Failed to start process!\n";
}
References: proc_open(), proc_close(), proc_get_status(), proc_terminate()
I am trying to replace the crontab using a new crontab stored at /tmp/crontab.txt.
$output = '';
$output .= "Existing Crontab contents:<br>";
$output .= shell_exec('crontab -l');
$output .= "<br>new contents:<br>";
$output .= file_get_contents('/tmp/crontab.txt');
$output .= "<br>Result of import:<br>";
$output .= shell_exec('crontab /tmp/crontab.txt');
$output .= shell_exec('crontab -l');
echo $output;
The output is:
Existing Crontab contents:
1 2 3 4 5 existing
new contents:
* * * * * echo 'test'
Result of import:
1 2 3 4 5 existing
You can see the import does not work and does not show an error.
Apache is running as 'nobody'. I have tried crontab -u nobody /tmp/crontab.txt as root and it works.
Is this a permissions issue? If so, why is php (running as nobody) unable to update it's own cron? How do I get around this?
Thanks
Try changing your import line to this:
$output .= shell_exec('crontab /tmp/crontab.txt 2>&1');
that'll redirect stderr to stdout and let PHP catch any error message cron's spitting out.