|
|
| |
Schedule::Cron(3) |
User Contributed Perl Documentation |
Schedule::Cron(3) |
Cron - cron-like scheduler for Perl subroutines
use Schedule::Cron;
# Subroutines to be called
sub dispatcher {
print "ID: ",shift,"\n";
print "Args: ","@_","\n";
}
sub check_links {
# do something...
}
# Create new object with default dispatcher
my $cron = new Schedule::Cron(\&dispatcher);
# Load a crontab file
$cron->load_crontab("/var/spool/cron/perl");
# Add dynamically crontab entries
$cron->add_entry("3 4 * * *",ROTATE => "apache","sendmail");
$cron->add_entry("0 11 * * Mon-Fri",\&check_links);
# Run scheduler
$cron->run(detach=>1);
This module provides a simple but complete cron like scheduler. I.e this module
can be used for periodically executing Perl subroutines. The dates and
parameters for the subroutines to be called are specified with a format known
as crontab entry (see "METHODS",
"add_entry()" and crontab(5))
The philosophy behind
"Schedule::Cron" is to call subroutines
periodically from within one single Perl program instead of letting
"cron" trigger several (possibly
different) Perl scripts. Everything under one roof. Furthermore,
"Schedule::Cron" provides mechanism to
create crontab entries dynamically, which isn't that easy with
"cron".
"Schedule::Cron" knows about all
extensions (well, at least all extensions I'm aware of, i.e those of the so
called "Vixie" cron) for crontab entries like ranges including
'steps', specification of month and days of the week by name, or coexistence
of lists and ranges in the same field. It even supports a bit more (like
lists and ranges with symbolic names).
- $cron = new Schedule::Cron($dispatcher,[extra args])
- Creates a new "Cron" object.
$dispatcher is a reference to a subroutine, which
will be called by default. $dispatcher will be
invoked with the arguments parameter provided in the crontab entry if no
other subroutine is specified. This can be either a single argument
containing the argument parameter literally has string (default behavior)
or a list of arguments when using the
"eval" option described below.
The date specifications must be either provided via a crontab
like file or added explicitly with
"add_entry()"
("add_entry").
extra_args can be a hash or hash reference for
additional arguments. The following parameters are recognized:
- file => <crontab>
- Load the crontab entries from <crontab>
- eval => 1
- Eval the argument parameter in a crontab entry before calling the
subroutine (instead of literally calling the dispatcher with the argument
parameter as string)
- nofork => 1
- Don't fork when starting the scheduler. Instead, the jobs are executed
within current process. In your executed jobs, you have full access to the
global variables of your script and hence might influence other jobs
running at a different time. This behaviour is fundamentally different to
the 'fork' mode, where each jobs gets its own process and hence a
copy of the process space, independent of each other job and the
main process. This is due to the nature of the
"fork" system call.
- nostatus => 1
- Do not update status in $0. Set this if you don't
want ps to reveal the internals of your application, including job
argument lists. Default is 0 (update status).
- skip => 1
- Skip any pending jobs whose time has passed. This option is only useful in
combination with "nofork" where a job
might block the execution of the following jobs for quite some time. By
default, any pending job is executed even if its scheduled execution time
has already passed. With this option set to true all pending which would
have been started in the meantime are skipped.
- catch => 1
- Catch any exception raised by a job. This is especially useful in
combination with the "nofork" option to
avoid stopping the main process when a job raises an exception
(dies).
- after_job => \&after_sub
- Call a subroutine after a job has been run. The first argument is the
return value of the dispatched job, the reminding arguments are the
arguments with which the dispatched job has been called.
Example:
my $cron = new Schedule::Cron(..., after_job => sub {
my ($ret,@args) = @_;
print "Return value: ",$ret," - job arguments: (",join ":",@args,")\n";
});
- log => \&log_sub
- Install a logging subroutine. The given subroutine is called for several
events during the lifetime of a job. This method is called with two
arguments: A log level of 0 (info),1 (warning) or 2 (error) depending on
the importance of the message and the message itself.
For example, you could use Log4perl
(<http://log4perl.sf.net>) for logging purposes for example like
in the following code snippet:
use Log::Log4perl;
use Log::Log4perl::Level;
my $log_method = sub {
my ($level,$msg) = @_;
my $DBG_MAP = { 0 => $INFO, 1 => $WARN, 2 => $ERROR };
my $logger = Log::Log4perl->get_logger("My::Package");
$logger->log($DBG_MAP->{$level},$msg);
}
my $cron = new Schedule::Cron(.... , log => $log_method);
- loglevel => <-1,0,1,2>
- Restricts logging to the specified severity level or below. Use 0 to have
all messages generated, 1 for only warnings and errors and 2 for errors
only. Default is 0 (all messages). A loglevel of -1 (debug) will include
job argument lists (also in $0) in the job start
message logged with a level of 0 or above. You may have security concerns
with this. Unless you are debugging, use 0 or higher. A value larger than
2 will disable logging completely.
Although you can filter in your log routine, generating the
messages can be expensive, for example if you pass arguments pointing to
large hashes. Specifying a loglevel avoids formatting data that your
routine would discard.
- processprefix => <name>
- Cron::Schedule sets the process' name (i.e. $0) to
contain some informative messages like when the next job executes or with
which arguments a job is called. By default, the prefix for this labels is
"Schedule::Cron". With this option you
can set it to something different. You can e.g. use
$0 to include the original process name. You can
inhibit this with the "nostatus" option,
and prevent the argument display by setting
"loglevel" to zero or higher.
- sleep => \&hook
- If specified, &hook will be called instead of sleep(), with the
time to sleep in seconds as first argument and the Schedule::Cron object
as second. This hook allows you to use select() instead of sleep,
so that you can handle IO, for example job requests from a network
connection.
e.g.
$cron->run( { sleep => \&sleep_hook, nofork => 1 } );
sub sleep_hook {
my ($time, $cron) = @_;
my ($rin, $win, $ein) = ('','','');
my ($rout, $wout, $eout);
vec($rin, fileno(STDIN), 1) = 1;
my ($nfound, $ttg) = select($rout=$rin, $wout=$win, $eout=$ein, $time);
if ($nfound) {
handle_io($rout, $wout, $eout);
}
return;
}
- $cron->load_crontab($file)
- $cron->load_crontab(file=>$file,[eval=>1])
- Loads and parses the crontab file $file. The
entries found in this file will be added to the current time table
with "$cron->add_entry".
The format of the file consists of cron commands containing of
lines with at least 5 columns, whereas the first 5 columns specify the
date. The rest of the line (i.e columns 6 and greater) contains the
argument with which the dispatcher subroutine will be called. By
default, the dispatcher will be called with one single string argument
containing the rest of the line literally. Alternatively, if you call
this method with the optional argument
"eval=>1" (you must then use the
second format shown above), the rest of the line will be evaled before
used as argument for the dispatcher.
For the format of the first 5 columns, please see
"add_entry".
Blank lines and lines starting with a
"#" will be ignored.
There's no way to specify another subroutine within the
crontab file. All calls will be made to the dispatcher provided at
construction time.
If you want to start up fresh, you should call
"$cron->clean_timetable()"
before.
Example of a crontab fiqw(le:)
# The following line runs on every Monday at 2:34 am
34 2 * * Mon "make_stats"
# The next line should be best read in with an eval=>1 argument
* * 1 1 * { NEW_YEAR => '1',HEADACHE => 'on' }
- $cron->add_entry($timespec,[arguments])
- Adds a new entry to the list of scheduled cron jobs.
Time and Date specification
$timespec is the specification of the
scheduled time in crontab format (crontab(5)) which contains five
mandatory time and date fields and an optional 6th column.
$timespec can be either a plain string, which
contains a whitespace separated time and date specification.
Alternatively, $timespec can be a reference to
an array containing the five elements for the date fields.
The time and date fields are (taken mostly from
crontab(5), "Vixie" cron):
field values
===== ======
minute 0-59
hour 0-23
day of month 1-31
month 1-12 (or as names)
day of week 0-7 (0 or 7 is Sunday, or as names)
seconds 0-59 (optional)
A field may be an asterisk (*), which always stands for
``first-last''.
Ranges of numbers are allowed. Ranges are two numbers
separated with a hyphen. The specified range is
inclusive. For example, 8-11 for an ``hours'' entry
specifies execution at hours 8, 9, 10 and 11.
Lists are allowed. A list is a set of numbers (or
ranges) separated by commas. Examples: ``1,2,5,9'',
``0-4,8-12''.
Step values can be used in conjunction with ranges.
Following a range with ``/<number>'' specifies skips of
the numbers value through the range. For example,
``0-23/2'' can be used in the hours field to specify
command execution every other hour (the alternative in
the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22'').
Steps are also permitted after an asterisk, so if you
want to say ``every two hours'', just use ``*/2''.
Names can also be used for the ``month'' and ``day of
week'' fields. Use the first three letters of the
particular day or month (case doesn't matter).
Note: The day of a command's execution can be specified
by two fields -- day of month, and day of week.
If both fields are restricted (ie, aren't *), the
command will be run when either field matches the
current time. For example, ``30 4 1,15 * 5''
would cause a command to be run at 4:30 am on the
1st and 15th of each month, plus every Friday
Examples:
"8 0 * * *" ==> 8 minutes after midnight, every day
"5 11 * * Sat,Sun" ==> at 11:05 on each Saturday and Sunday
"0-59/5 * * * *" ==> every five minutes
"42 12 3 Feb Sat" ==> at 12:42 on 3rd of February and on
each Saturday in February
"32 11 * * * 0-30/2" ==> 11:32:00, 11:32:02, ... 11:32:30 every
day
In addition, ranges or lists of names are allowed.
An optional sixth column can be used to specify the seconds
within the minute. If not present, it is implicitely set to
"0".
Command specification
The subroutine to be executed when the the
$timespec matches can be specified in several
ways.
First, if the optional
"arguments" are lacking, the default
dispatching subroutine provided at construction time will be called
without arguments.
If the second parameter to this method is a reference to a
subroutine, this subroutine will be used instead of the dispatcher.
Any additional parameters will be given as arguments to the
subroutine to be executed. You can also specify a reference to an array
instead of a list of parameters.
You can also use a named parameter list provided as an
hashref. The named parameters recognized are:
- subroutine
- sub
- Reference to subroutine to be executed
- arguments
- args
- Reference to array containing arguments to be use when calling the
subroutine
- eval
- If true, use the evaled string provided with the
"arguments" parameter. The evaluation
will take place immediately (not when the subroutine is going to be
called)
Examples:
$cron->add_entry("* * * * *");
$cron->add_entry("* * * * *","doit");
$cron->add_entry("* * * * *",\&dispatch,"first",2,"third");
$cron->add_entry("* * * * *",{'subroutine' => \&dispatch,
'arguments' => [ "first",2,"third" ]});
$cron->add_entry("* * * * *",{'subroutine' => \&dispatch,
'arguments' => '[ "first",2,"third" ]',
'eval' => 1});
- @entries = $cron->list_entries()
- Return a list of cron entries. Each entry is a hash reference of the
following form:
$entry = {
time => $timespec,
dispatch => $dispatcher,
args => $args_ref
}
Here $timespec is the specified time
in crontab format as provided to
"add_entry",
$dispatcher is a reference to the dispatcher for
this entry and $args_ref is a reference to an
array holding additional arguments (which can be an empty array
reference). For further explanation of this arguments refer to the
documentation of the method
"add_entry".
The order index of each entry can be used within
"update_entry",
"get_entry" and
"delete_entry". But be aware, when you
are deleting an entry, that you have to refetch the list, since the
order will have changed.
Note that these entries are returned by value and were
opbtained from the internal list by a deep copy. I.e. you are free to
modify it, but this won't influence the original entries. Instead use
"update_entry" if you need to modify
an exisiting crontab entry.
- $entry = $cron->get_entry($idx)
- Get a single entry. $entry is either a hashref
with the possible keys "time",
"dispatch" and
"args" (see
"list_entries()") or undef if no entry
with the given index $idx exists.
- $cron->delete_entry($idx)
- Delete the entry at index $idx. Returns the
deleted entry on success, "undef"
otherwise.
- $cron->update_entry($idx,$entry)
- Updates the entry with index $idx.
$entry is a hash ref as descibed in
"list_entries()" and must contain at
least a value "$entry->{time}". If no
"$entry->{dispatcher}" is given, then
the default dispatcher is used. This method returns the old entry on
success, "undef" otherwise.
- $cron->run([options])
- This method starts the scheduler.
When called without options, this method will never return and
executes the scheduled subroutine calls as needed.
Alternatively, you can detach the main scheduler loop from the
current process (daemon mode). In this case, the pid of the forked
scheduler process will be returned.
The "options" parameter
specifies the running mode of
"Schedule::Cron". It can be either a
plain list which will be interpreted as a hash or it can be a reference
to a hash. The following named parameters (keys of the provided hash)
are recognized:
- detach
- If set to a true value the scheduler process is detached from the current
process (UNIX only).
- pid_file
- If running in daemon mode, name the optional file, in which the process id
of the scheduler process should be written. By default, no PID File will
be created.
- nofork, skip, catch, log, loglevel, nostatus, sleep
- See "new()" for a description of these
configuration parameters, which can be provided here as well. Note, that
the options given here overrides those of the constructor.
Examples:
# Start scheduler, detach from current process and
# write the PID of the forked scheduler to the
# specified file
$cron->run(detach=>1,pid_file=>"/var/run/scheduler.pid");
# Start scheduler and wait forever.
$cron->run();
- $cron->clean_timetable()
- Remove all scheduled entries
- $cron->check_entry($id)
- Check, whether the given ID is already registered in the timetable. A ID
is the first argument in the argument parameter of the a crontab entry.
Returns (one of) the index in the timetable (can be 0, too) if
the ID could be found or "undef"
otherwise.
Example:
$cron->add_entry("* * * * *","ROTATE");
.
.
defined($cron->check_entry("ROTATE")) || die "No ROTATE entry !"
- $cron->get_next_execution_time($cron_entry,[$ref_time])
- Well, this is mostly an internal method, but it might be useful on its
own.
The purpose of this method is to calculate the next execution
time from a specified crontab entry
Parameters:
- $cron_entry
- The crontab entry as specified in "add_entry"
- $ref_time
- The reference time for which the next time should be searched which
matches $cron_entry. By default, take the current
time
This method returns the number of epoch-seconds of the next
matched date for $cron_entry.
Since I suspect, that this calculation of the next execution time
might fail in some circumstances (bugs are lurking everywhere ;-) an
additional interactive method "bug()" is
provided for checking crontab entries against your expected output. Refer to
the top-level README for additional usage information for this method.
Daylight saving occurs typically twice a year: In the first switch, one hour is
skipped. Any job which which triggers in this skipped hour will be fired in
the next hour. So, when the DST switch goes from 2:00 to 3:00 a job which is
scheduled for 2:43 will be executed at 3:43.
For the reverse backwards switch later in the year, the behaviour
is undefined. Two possible behaviours can occur: For jobs triggered in short
intervals, where the next execution time would fire in the extra hour as
well, the job could be executed again or skipped in this extra hour.
Currently, running "Schedule::Cron" in
"MET" would skip the extra job, in
"PST8PDT" it would execute a second time.
The reason is the way how Time::ParseDate calculates epoch times for dates
given like "02:50:00 2009/10/25". Should
it return the seconds since 1970 for this time happening 'first', or for
this time in the extra hour ? As it turns out, Time::ParseDate returns the
epoch time of the first occurence for
"PST8PDT" and for
"MET" it returns the second occurence.
Unfortunately, there is no way to specify which entry Time::ParseDate
should pick (until now). Of course, after all, this is obviously not
Time::ParseDate's fault, since a simple date specification within the DST
backswitch period is ambigious. However, it would be nice if the
parsing behaviour of Time::ParseDate would be consistent across time zones
(a ticket has be raised for fixing this). Then Schedule::Cron's behaviour
within a DST backward switch would be consistent as well.
Since changing the internal algorithm which worked now for over
ten years would be too risky and I don't see any simple solution for this
right now, it is likely that this undefined behaviour will exist for
some time. Maybe some hero is coming along and will fix this, but this is
probably not me ;-)
Sorry for that.
Copyright 1999-2011 Roland Huss.
This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
Visit the GSP FreeBSD Man Page Interface. Output converted with ManDoc. |