Page 1 of 2

fake cron - revised

Posted: Sat Aug 18, 2007 10:29 pm
by s.dot
[edit]

Posted: Sun Aug 19, 2007 12:59 am
by roast
Thanks fro your wonderful idea. :D

But,it seems doesn't work as anticipation,and I get the error message as below:

Code: Select all

Fatal error: Call to undefined method fakeCron::_evaluateCronFormatTime() in D:\www\cron\fakecron.class.php on line 231

Posted: Sun Aug 19, 2007 1:17 am
by s.dot
Yes. That is the method that I have not written yet. It's the last one I have to write. I didn't want to complete the piece before it was critiqued. However, if you pass an english time to it like '4 PM', or an array of times, you will see that it works. ;)

EDIT| Which I will write in the morning, because I'm pooped right now (2:30 AM). I'll then probably refactor a little bit, write some documentation, do some tests, and call it a release!

Posted: Sun Aug 19, 2007 9:37 am
by VladSun
1. Fork process for every item in the cron list. Otherwise one zombie cron job process will make your fakecron a zombie and no more cron jobs will be executed ;)
2. I repeat myself - still there is a need to add/remove cron jobs in real time.
3. In you current version - why sleep period is 1min, when the only available cron job period is 1 hour?
4. Better error/warning logging and log format.

PS:

5. I think it would be much better if you separate the daemon and the cron job list by using config file instead. Then you will need one additional method - to check if the config file has been updated. It will the "add/remove cron job in real time" verryyy easy :)

Posted: Sun Aug 19, 2007 12:50 pm
by s.dot
VladSun wrote:1. Fork process for every item in the cron list. Otherwise one zombie cron job process will make your fakecron a zombie and no more cron jobs will be executed ;)
2. I repeat myself - still there is a need to add/remove cron jobs in real time.
3. In you current version - why sleep period is 1min, when the only available cron job period is 1 hour?
4. Better error/warning logging and log format.

PS:

5. I think it would be much better if you separate the daemon and the cron job list by using config file instead. Then you will need one additional method - to check if the config file has been updated. It will the "add/remove cron job in real time" verryyy easy :)
1. I'm not sure how to fork processes.
2. I may do this. Load a text file of tasks. md5_file() the contents and check for changes during each iteration.
3. Available cron job period is every minute by using * * * * *. The only thing the hour is for, is to generate a list of tasks to run every hour -- has nothing to do with how often tasks can be run. If you set a task to run every minute, the script will stores this to run 60 times during this hour.
4. What do you suggest?
5. I'm starting to agree. I'm going to finish this version first, then work on that.

Posted: Sun Aug 19, 2007 7:07 pm
by VladSun
1. Well, if you refer back to the original topic you will see how to do it ;) viewtopic.php?t=72201. You need two .php files - master and child. The "master" PHP will iterate through your cron list and execute the "child" PHP with arguments according to the cron job item.

main.php

Code: Select all

<?php
while (true)
{
       if ($cion_job_1)
       {
              shell_exec("php -q child.php '$params_1' &");
       }
       sleep($sleep_time);
}
?>
child.php

Code: Select all

<?php
        $err = shell_exec("php -q $params");
?>
2. Refer to (5)
3. If it's so - OK :)
4. Instead of using "die" you may use a function to write to a specified file - e.g. /etc/fakecron.log.
5. :) I am glad that you agree :) My suggestion is NOT to use XML (or similar) file format. Tab-delimited config would be enough.

PS: Read the crontab/crond manual - there you will find a lot usefull things :)

Posted: Sun Aug 19, 2007 7:12 pm
by s.dot
Alrighty, I've got the script completely working now, even with crontab style formats.
It feels really ugly. So I'm going to write a "final" version, including the real time add/edit task feature. That will be nice because there won't need to be starting/stopping of the daemon. Logging could be added too.

I have spent lots of time on this. I hope it will be useful. :)

Posted: Mon Aug 20, 2007 4:49 pm
by s.dot
I am almost done writing the "final" version of this. I have logging enabled, realtime add/edit scheduled task list. I'm stuck on the part on how to fork a new process to avoid a zombie task from zombie'ing the fakeCron daemon.

Would I put it in a new class and call it like this?

Code: Select all

if (taskToRun)
{
    $output = new runScript(taskToRun);
}
Would that make it fork a new process?

I can't really make sense of how the main.php and child.php code posted by Vladsun works?

Posted: Mon Aug 20, 2007 4:52 pm
by feyd
http://php.net/ref.pcntl may be of interest.

Posted: Mon Aug 20, 2007 4:59 pm
by s.dot
Thanks for that link. I did some quick reading and it seems it's not what I'm looking for.
Process Control support in PHP is not enabled by default. You have to compile the CGI or CLI version of PHP with --enable-pcntl configuration option when compiling PHP to enable Process Control support.
A main target for this script would be people using shared servers, who don't have control over the php configuration.
Note: Currently, this module will not function on non-Unix platforms (Windows).
Also, I want it to work on windows, for users not having access to crontab and don't know how to (or want to) run windows task scheduler.

Posted: Mon Aug 20, 2007 5:04 pm
by feyd
Windows comes with the "at" command. Executing a command via system() et al can be redirected, this effectively separates the process too.

Posted: Mon Aug 20, 2007 5:10 pm
by VladSun
@feyd
Installation

Process Control support in PHP is not enabled by default. You have to compile the CGI or CLI version of PHP with --enable-pcntl configuration option when compiling PHP to enable Process Control support.

Note: Currently, this module will not function on non-Unix platforms (Windows).
Recently, I found a script which should work on *nix and Window OS:

Code: Select all

<?php

  $runCommand = "YOUR COMMAND AND ITS PARAMS HERE";

  if(isset($_SERVER['PWD'])//*nix (aka NOT windows)
  {
    $nullResult = `$runCommand > /dev/null &`;
  }
  else //windowz
  {
    $WshShell = new COM("WScript.Shell");
    $oExec = $WshShell->Run($runCommand, 7, false);
  }

?>
You suggested to check PHP_OS (it's a constant) over a $_SERVER variable.

@scottayy

The contents of main.php should be a part of your class. It should pass as argument to child.php a string containing cron job php file name and its arguments. This should replace the following line code (and its related) in your method "public function run()"

Code: Select all

$cronOutput = shell_exec(escapeshellcmd('php -q -f ' . $cronToRun['script']));
You have to replace it with something like this:

Code: Select all

if(isset($_SERVER['PWD'])//*nix (aka NOT windows)
  {
    $nullResult = escapeshellcmd('php -q -f child.php "' . $cronToRun['script']. '" > /dev/null &');
  }
  else //windowz
  {
    $WshShell = new COM("WScript.Shell");
    $oExec = $WshShell->Run(escapeshellcmd('php -q -f child.php "' . $cronToRun['script']) . '"', 7, false);
  }
In child.php you have to implement the real execution of $cronToRun['script']. You can get the arguments passed to child.php by using $argv[].

Posted: Mon Aug 20, 2007 5:41 pm
by s.dot
This script is killing me :evil:
break time

Posted: Mon Aug 20, 2007 5:54 pm
by VladSun
:) Let me help you:

fakeCron.class.php

Code: Select all

<?php
public function run()
    {
        //set script to run "forever"
        ignore_user_abort(true);
        set_time_limit(0);
       
        //infinite loop
        while (true) {
           
            //check if new hourly cron list needs to be generated
            $this->_previousHour = $this->_currentHour;
            $this->_currentHour = date("G");
           
            if (($this->_previousHour == null) || ($this->_previousHour != $this->_currentHour)) {
                $this->_generateHourlyCronList();
            }
           
            //if there are scripts to be run this hour, loop through them
            if(!empty($this->_cronsToRun)) {
                foreach ($this->_cronsToRun AS $cronToRun) {
                   
                    //see if script should be run now
                    if (($cronToRun['time'] <= time()) && ($cronToRun['time'] >= (time()-$this->_timeout))) {
                       
                        //check if script exists
                        if (file_exists($cronToRun['script'])) {
							if(isset($_SERVER['PWD']) { //*nix (aka NOT windows)
								$nullResult = escapeshellcmd('php -q -f forkCron.php "' . $cronToRun['script']. '" > /dev/null &');
							}
							else { //windowz 
								$WshShell = new COM("WScript.Shell");
								$nullResult = $WshShell->Run(escapeshellcmd('php -q -f forkCron.php "' . $cronToRun['script']) . '"', 7, false);
							}                             
                        } else {
                            //script could not be found -- send error email?
                            if ($this->emailErrors == true) {
                                $this->_sendErrorMail($cronToRun['script'], time(), 'Could not locate script on server.');
                            }
                        }
                    }
                }
            }
           
            //check if fakeCron has been killed
            $this->_checkKill();
           
            //update file, so fakecron can be viewed as "running"
            $this->_updateFile();
           
            //sleep to avoid CPU hogging, never ending, evil loop =]
            sleep($this->_timeout);
        }
    } 

?>
forkCron.php

Code: Select all

//run the script and capture the output
	if (!empty($argv[1]))
	{
		$cronOutput = shell_exec(escapeshellcmd('php -q -f ' . $argv[1]));
		echo $cronOutput;
	}
This code will break your checks for "execution time" > "timeout", but it gives you the idea. If you want these check to be performed you have to implement a "feedback" messaging from forkCron to fakeCron. That's why there are two files. If you don't want to have these checks you may use only fakeCron.class.php by removing the "forkCron.php" string from the escapeshellcmd's arguments.

Posted: Mon Aug 20, 2007 7:26 pm
by s.dot
Just wondering... does anyone see a need for this? Or am I being overly optimistic in the hopes that it will be useful? :P

Something inside of me wants to write something great & open source. But the complexities in this makes me think this isn't it. :P

/end cheesyness