ConLite/conlite/includes/pseudo-cron.inc.php

511 Zeilen
17 KiB
PHP

<?php
/* * *************************************************************************
pseudo-cron v1.2.1.con // modified version for contenido
(c) 2003 Kai Blankenhorn
www.bitfolge.de/en
kaib@bitfolge.de
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* ***************************************************************************
Usually regular tasks like backup up the site's database are run using cron
jobs. With cron jobs, you can exactly plan when a certain command is to be
executed. But most homepage owners can't create cron jobs on their web
server - providers demand some extra money for that.
The only thing that's certain to happen quite regularly on a web page are
page requests. This is where pseudo-cron comes into play: With every page
request it checks if any cron jobs should have been run since the previous
request. If there are, they are run and logged.
Pseudo-cron uses a syntax very much like the Unix cron's one. For an
overview of the syntax used, see a page of the UNIXGEEKS. The syntax
pseudo-cron uses is different from the one described on that page in
the following points:
- there is no user column
- the executed command has to be an include()able file (which may contain further PHP code)
All job definitions are made in a text file on the server with a
user-definable name. A valid command line in this file is, for example:
* 2 1,15 * * samplejob.inc.php
This runs samplejob.inc.php at 2am on the 1st and 15th of each month.
Features:
- runs any PHP script
- periodical or time-controlled script execution
- logs all executed jobs
- can be run from an IMG tag in an HTML page
- follow Unix cron syntax for crontabs
Usage:
- Modify the variables in the config section below to match your server.
- Write a PHP script that does the job you want to be run regularly. Be
sure that any paths in it are relative to the script that will run
pseudo-cron in the end.
- Set up your crontab file with your script
- Wait for the next scheduled run :)
Note:
You can log messages to pseudo-cron's log file by calling
logMessage("log a message", $PC_writeDir, $PC_useLog, $PC_debug);
Changelog:
v1.2.1.con 11-28-03
changed: removed all global variables
changed: renamed intern variables
changed: intern function calls
changed: extended debug information
modified by horwath@opensa.org
v1.2.1 02-03-03
fixed: jobs may be run too often under certain conditions
added: global debug switch
changed: typo in imagecron.php which prevented it from working
v1.2 01-31-03
added: more documentation
changed: log file should now be easier to use
changed: log file name
v1.1 01-29-03
changed: renamed pseudo-cron.php to pseudo-cron.inc.php
fixed: comments at the end of a line don't work
fixed: empty lines in crontab file create nonsense jobs
changed: log file grows big very quickly
changed: included config file in main file to avoid directory confusion
added: day of week abbreviations may now be used (three letters, english)
v1.0 01-17-03
inital release
* ************************************************************************* */
if (!defined('CON_FRAMEWORK')) {
die('Illegal call');
}
/* * ************************************* */
/* config section */
/* * ************************************* */
// || PLEASE NOTE:
// || all paths used here and in cron scripts
// || must be absolute or relative to the script which includes pseudo-cron.inc.php!
// the file that contains the job descriptions
// for a description of the format, see http://www.unixgeeks.org/security/newbie/unix/cron-1.html
// and http://www.bitfolge.de/pseudocron
$PC_cronTab = $cfg['path']['conlite_cronlog'] . "crontab.txt";
$PC_localCronTab = $cfg['path']['conlite_cronlog'] . "crontab.local.txt";
$PC_writeDir = $cfg['path']['conlite_cronlog'];
$PC_jobDir = $cfg['path']['conlite'] . $cfg['path']['cronjobs'];
// store directory information
$PC_reqDir = getcwd();
// control logging, 1=use log file, 0=don't use log file
$PC_useLog = $cfg["pseudocron"]["logging"];
// turn on / off debugging output
// DO NOT use this on live servers!
$PC_debug = false;
/* * ************************************* */
/* don't change anything here */
/* * ************************************* */
define("PC_MINUTE", 1);
define("PC_HOUR", 2);
define("PC_DOM", 3);
define("PC_MONTH", 4);
define("PC_DOW", 5);
define("PC_CMD", 7);
define("PC_CRONLINE", 8);
if ($PC_debug) {
echo "<pre>Configuration:";
echo "\nPC_cronTab = " . $PC_cronTab;
echo "\nPC_writeDir = " . $PC_writeDir;
echo "\nPC_jobDir = " . $PC_jobDir;
echo "\nPC_reqDir = " . $PC_reqDir;
echo "\nPC_useLog = " . $PC_useLog;
echo "\n";
}
global $bJobRunned;
$bJobRunned = false;
chdir($PC_jobDir);
$PC_jobs = parseCronFile($PC_cronTab, $PC_debug);
for ($i = 0; $i < count($PC_jobs); $i++) {
$bJobRunned = true;
runJob($PC_jobs[$i], $PC_jobDir, $PC_writeDir, $PC_useLog, $PC_debug);
}
$PC_jobs = parseCronFile($PC_localCronTab, $PC_debug);
for ($i = 0; $i < count($PC_jobs); $i++) {
$bJobRunned = true;
runJob($PC_jobs[$i], $PC_jobDir, $PC_writeDir, $PC_useLog, $PC_debug);
}
chdir($PC_reqDir);
if ($PC_debug)
echo "\n</pre>";
function logMessage($msg, $PC_writeDir, $PC_useLog, $PC_debug) {
$oCronLog = new cCronJob();
$oCronLog->useCronLog($PC_useLog);
$oCronLog->logMessages($msg);
unset($oCronLog);
if ($PC_debug) {
echo $msg;
}
}
function lTrimZeros($number) {
while ($number[0] == '0') {
$number = substr($number, 1);
}
return (is_numeric($number))?$number:0;
}
function parseElement($element, &$targetArray, $numberOfElements) {
$subelements = explode(",", $element);
for ($i = 0; $i < $numberOfElements; $i++) {
$targetArray[$i] = $subelements[0] == "*";
}
for ($i = 0; $i < count($subelements); $i++) {
if (preg_match("~^(\\*|([0-9]{1,2})(-([0-9]{1,2}))?)(/([0-9]{1,2}))?$~", $subelements[$i], $matches)) {
if ($matches[1] == "*") {
$matches[2] = 0; // from
$matches[4] = $numberOfElements; //to
} elseif (!array_key_exists(4, $matches) || $matches[4] == "") {
$matches[4] = $matches[2];
}
if (array_key_exists(5, $matches)) {
if ($matches[5][0] != "/") {
$matches[6] = 1; // step
}
} else {
$matches[6] = 1; // step
}
for ($j = lTrimZeros($matches[2]); $j <= lTrimZeros($matches[4]); $j += lTrimZeros($matches[6])) {
$targetArray[$j] = TRUE;
}
}
}
}
function decDate(&$dateArr, $amount, $unit, $PC_debug) {
if ($PC_debug)
echo sprintf("Decreasing from %02d.%02d. %02d:%02d by %d %6s ", $dateArr[mday], $dateArr[mon], $dateArr[hours], $dateArr[minutes], $amount, $unit);
if ($unit == "mday") {
$dateArr["hours"] = 23;
$dateArr["minutes"] = 59;
$dateArr["seconds"] = 59;
$dateArr["mday"] -= $amount;
$dateArr["wday"] -= $amount % 7;
if ($dateArr["wday"] < 0) {
$dateArr["wday"] += 7;
}
if ($dateArr["mday"] < 1) {
$dateArr["mon"] --;
switch ($dateArr["mon"]) {
case 0:
$dateArr["mon"] = 12;
$dateArr["year"] --;
// fall through
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
$dateArr["mday"] = 31;
break;
case 4:
case 6:
case 9:
case 11:
$dateArr["mday"] = 30;
break;
case 2:
$dateArr["mday"] = 28;
break;
}
}
} elseif ($unit == "hour") {
if ($dateArr["hours"] == 0) {
decDate($dateArr, 1, "mday", $PC_debug);
} else {
$dateArr["minutes"] = 59;
$dateArr["seconds"] = 59;
$dateArr["hours"] --;
}
} elseif ($unit == "minute") {
if ($dateArr["minutes"] == 0) {
decDate($dateArr, 1, "hour", $PC_debug);
} else {
$dateArr["seconds"] = 59;
$dateArr["minutes"] --;
}
}
if ($PC_debug)
echo sprintf("to %02d.%02d. %02d:%02d\n", $dateArr[mday], $dateArr[mon], $dateArr[hours], $dateArr[minutes]);
}
function getLastScheduledRunTime($job, $PC_debug) {
$dateArr = getdate();
$minutesBack = 0;
while (
$minutesBack < 525600 AND ( !$job[PC_MINUTE][$dateArr["minutes"]] OR ! $job[PC_HOUR][$dateArr["hours"]] OR ( !$job[PC_DOM][$dateArr["mday"]] OR ! $job[PC_DOW][$dateArr["wday"]]) OR ! $job[PC_MONTH][$dateArr["mon"]])
) {
if (!$job[PC_DOM][$dateArr["mday"]] OR ! $job[PC_DOW][$dateArr["wday"]]) {
decDate($dateArr, 1, "mday", $PC_debug);
$minutesBack += 1440;
continue;
}
if (!$job[PC_HOUR][$dateArr["hours"]]) {
decDate($dateArr, 1, "hour", $PC_debug);
$minutesBack += 60;
continue;
}
if (!$job[PC_MINUTE][$dateArr["minutes"]]) {
decDate($dateArr, 1, "minute", $PC_debug);
$minutesBack++;
continue;
}
}
if ($PC_debug)
print_r($dateArr);
return mktime($dateArr["hours"], $dateArr["minutes"], 0, $dateArr["mon"], $dateArr["mday"], $dateArr["year"]);
}
function getJobFileName($jobname, $PC_writeDir) {
$jobfile = $PC_writeDir . urlencode($jobname) . ".job";
return $jobfile;
}
function getLastActialRunTime($jobname, $PC_writeDir) {
$jobfile = getJobFileName($jobname, $PC_writeDir);
if (file_exists($jobfile)) {
$file = fopen($jobfile, "rb");
$lastRun = fgets($file, 100);
fclose($file);
if (is_numeric($lastRun)) {
return $lastRun;
}
}
return 0;
}
function markLastRun($jobname, $lastRun, $PC_writeDir) {
$jobfile = getJobFileName($jobname, $PC_writeDir);
if ($file = @fopen($jobfile, "w")) {
fputs($file, $lastRun);
fclose($file);
} else {
//echo "Could not write into file $jobfile - permission denied.";
}
}
function runJob($job, $PC_jobDir, $PC_writeDir, $PC_useLog, $PC_debug = false) {
global $cfg, $sess, $PC_logDir;
if (!is_writable($PC_writeDir)) {
return false;
}
$extjob = Array();
$jobfile = getJobFileName($job[PC_CMD], $PC_writeDir);
parseElement($job[PC_MINUTE], $extjob[PC_MINUTE], 60);
parseElement($job[PC_HOUR], $extjob[PC_HOUR], 24);
parseElement($job[PC_DOM], $extjob[PC_DOM], 31);
parseElement($job[PC_MONTH], $extjob[PC_MONTH], 12);
parseElement($job[PC_DOW], $extjob[PC_DOW], 7);
$lastActual = getLastActialRunTime($job[PC_CMD], $PC_writeDir);
$lastScheduled = getLastScheduledRunTime($extjob, $PC_debug);
//echo "LastSch: " . $lastScheduled . " ::: LastAct: " . $lastActual . "\n";
if ($lastScheduled > $lastActual) {
logMessage("Running " . $job[PC_CRONLINE], $PC_writeDir, $PC_useLog, $PC_debug);
logMessage(" Last run: " . date("r", $lastActual), $PC_writeDir, $PC_useLog, $PC_debug);
logMessage(" Last scheduled: " . date("r", $lastScheduled), $PC_writeDir, $PC_useLog, $PC_debug);
markLastRun($job[PC_CMD], $lastScheduled, $PC_writeDir);
if ($PC_debug) {
echo getcwd();
include($PC_jobDir . $job[PC_CMD]); // display errors only when debugging
} else {
if (is_object($sess)) {
$sess->freeze();
}
@include($PC_jobDir . $job[PC_CMD]); // any error messages are supressed
if (is_object($sess)) {
$sess->thaw();
}
}
logMessage("Completed " . $job[PC_CRONLINE], $PC_writeDir, $PC_useLog, $PC_debug);
return true;
} else {
if ($PC_debug) {
logMessage("Skipping " . $job[PC_CRONLINE], $PC_writeDir, $PC_useLog, $PC_debug);
logMessage(" Last run: " . date("r", $lastActual), $PC_writeDir, $PC_useLog, $PC_debug);
logMessage(" Last scheduled: " . date("r", $lastScheduled), $PC_writeDir, $PC_useLog, $PC_debug);
logMessage("Completed " . $job[PC_CRONLINE], $PC_writeDir, $PC_useLog, $PC_debug);
}
return false;
}
}
function parseCronFile($PC_cronTabFile, $PC_debug) {
$file = @file($PC_cronTabFile);
$job = Array();
$jobs = Array();
if(!is_countable($file)) {
return $jobs;
}
for ($i = 0; $i < count($file); $i++) {
if ($file[$i][0] != '#') {
// old regex, without dow abbreviations:
// if (preg_match("~^([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-7,/*]+|Sun|Mon|Tue|Wen|Thu|Fri|Sat)\\s+([^#]*)(#.*)?$~i",$file[$i],$job)) {
if (preg_match("~^([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-7,/*]+|(-|/|Sun|Mon|Tue|Wed|Thu|Fri|Sat)+)\\s+([^#]*)(#.*)?$~i", $file[$i], $job)) {
$jobNumber = count($jobs);
$jobs[$jobNumber] = $job;
if ($jobs[$jobNumber][PC_DOW][0] != '*' AND ! is_numeric($jobs[$jobNumber][PC_DOW])) {
$jobs[$jobNumber][PC_DOW] = str_replace(
Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"), Array(0, 1, 2, 3, 4, 5, 6), $jobs[$jobNumber][PC_DOW]);
}
$jobs[$jobNumber][PC_CMD] = trim($job[PC_CMD]);
$jobs[$jobNumber][PC_CRONLINE] = $file[$i];
}
}
}
if ($PC_debug) {
var_dump($jobs);
}
return $jobs;
}
class cCronJob {
protected $_sCronTabFileName = "crontab.txt";
protected $_sCronTabLocalFileName = "crontab.local.txt";
protected $_sLogFileName = "pseudo-cron.log";
protected $_sJobDir;
protected $_sCronDir;
protected $_sLogDir;
private $_bUseLog;
private $_iMaxLogFileSize = 1024;
public function __construct() {
$this->_sJobDir = cRegistry::getConfigValue('path', 'conlite') . cRegistry::getConfigValue('path', 'cronjobs');
$this->_sCronDir = cRegistry::getConfigValue('path', 'conlite_cronlog');
$this->_sLogDir = cRegistry::getConfigValue('path', 'conlite_logs');
}
public function runJob() {
}
public function useCronLog($bUse = TRUE) {
$this->_bUseLog = boolval($bUse);
}
public function logMessages($sMsg) {
if (!$this->_bUseLog) {
return;
}
if (is_dir($this->_sLogDir) && is_writable($this->_sLogDir)) {
$logfile = $this->_sLogDir . $this->_sLogFileName;
} else if (is_dir($this->_sCronDir) && is_writable($this->_sCronDir)) {
$logfile = $this->_sCronDir . $this->_sLogFileName;
} else {
cWarning(__FILE__, __LINE__, "PseudoCron: Cannot write cronlog file!");
return;
}
if ($sMsg[strlen($sMsg) - 1] != "\n") {
$sMsg .= "\r\n";
}
$this->_rotateLogFiles($logfile);
file_put_contents($logfile, $sMsg, FILE_APPEND);
}
private function _rotateLogFiles($sLogFile) {
if (file_exists($sLogFile) &&
filesize($sLogFile) >= $this->_iMaxLogFileSize * 1024) {
// rotate
$path_info = pathinfo($sLogFile);
$base_directory = $path_info['dirname'];
$base_name = $path_info['basename'];
$num_map = array();
foreach (new DirectoryIterator($base_directory) as $fInfo) {
if ($fInfo->isDot() || !$fInfo->isFile()) {
continue;
}
if (preg_match('/^' . $base_name . '\.?([0-9]*)$/', $fInfo->getFilename(), $matches)) {
$num = $matches[1];
$file2move = $fInfo->getFilename();
if ($num == '') {
$num = 0;
}
$num_map[$num] = $file2move;
}
}
krsort($num_map);
foreach ($num_map as $num => $file2move) {
$targetN = $num + 1;
if($targetN > 10) {
unlink($base_directory . DIRECTORY_SEPARATOR . $file2move);
continue;
}
rename($base_directory . DIRECTORY_SEPARATOR . $file2move, $base_directory . DIRECTORY_SEPARATOR .$base_name . '.' . $targetN);
}
}
}
}
?>