#!/usr/bin/perl -w # $Id: Cron.pm,v 1.3 2000/06/12 07:25:41 roland Exp $ =head1 NAME Cron - cron-like scheduler for Perl subroutines =head1 SYNOPSIS 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); =head1 DESCRIPTION This module provides a simple but complete cron like scheduler. I.e this modules 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 (L<"METHODS">, C and L) The philosophy behind C is to call subroutines periodically from within one single Perl program instead of letting C trigger several (possibly different) perl scripts. Everything under one roof. Furthermore C provides mechanism to create crontab entries dynamically, which isn't that easy with C. C 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. And even a bit more (like lists and ranges with symbolic names). =head1 METHODS =over 4 =cut #' package Schedule::Cron; use Time::ParseDate; use Data::Dumper; use strict; use vars qw($VERSION $DEBUG); use subs qw(dbg); $VERSION = q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/ && sprintf("%d.%02d",$1-1,$2 ); my $DEBUG = 0; my @WDAYS = qw( Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday ); my @ALPHACONV = ( { }, { }, { }, { qw(jan 1 feb 2 mar 3 apr 4 may 5 jun 6 jul 7 aug 8 sep 9 oct 10 nov 11 dec 12) }, { qw(sun 0 mon 1 tue 2 wed 3 thu 4 fri 5 sat 6)} ); my @RANGES = ( [ 0,59 ], [ 0,23 ], [ 0,31 ], [ 0,12 ], [ 0,7 ] ); my @LOWMAP = ( {}, {}, { 0 => 1}, { 0 => 1}, { 7 => 0} ); sub REAPER { my $waitedpid = 0; while($waitedpid != -1) { $waitedpid = wait; } $SIG{CHLD} = \&REAPER; } $SIG{CHLD} = \&REAPER; =item $cron = new Schedule::Cron($dispatcher,[extra args]) Creates a new C object. C<$dispatcher> is a reference to a subroutine, which will be called by default. C<$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 C option described below. The date specifications must be either provided via a crontab like file or added explicitly with C (L<"add_entry">). I can be a hash or hash reference for additional arguments. The following parameters are recognized: file => Load the crontab entries from 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 =cut sub new { my $class = shift; my $dispatcher = shift || die "No dispatching sub provided"; die "Dispatcher not a ref to a subroutine" unless ref($dispatcher) eq "CODE"; my $cfg = ref($_[0]) eq "HASH" ? $_[0] : { @_ }; my $self = { cfg => $cfg, dispatcher => $dispatcher, queue => [ ], map => { } }; bless $self,(ref($class) || $class); $self->load_crontab if $cfg->{file}; $self; } =item $cron->load_crontab($file) =item $cron->load_crontab(file=>$file,[eval=>1]) Loads and parses the crontab file C<$file>. The entries found in this file will be B to the current time table with C<$cron-Eadd_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 C1> (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 L<"add_entry">. Blank lines and lines starting with a C<#> 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 C<$cron-Eclean_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' } =cut #' sub load_crontab { my $self = shift; my $cfg = shift; if ($cfg) { if (@_) { $cfg = ref($cfg) eq "HASH" ? $cfg : { $cfg,@_ }; } elsif (!ref($cfg)) { my $new_cfg = { }; $new_cfg->{file} = $cfg; $cfg = $new_cfg; } } my $file = $cfg->{file} || $self->{cfg}->{file} || die "No filename provided"; my $eval = $cfg->{eval} || $self->{cfg}->{eval}; open(F,$file) || die "Cannot open schedule $file : $!"; my $line = 0; while () { $line++; next if /^$/; next if /^\s*#/; chomp; s/\s*(.*)\s*$/$1/; my ($min,$hour,$dmon,$month,$dweek,$args) = split (/\s+/,$_,6); my $time = [ $min,$hour,$dmon,$month,$dweek ]; $self->add_entry($time,{ 'args' => $args, 'eval' => $eval}); } close F; } =item $cron->add_entry($timespec,[arguments]) Adds a new entry to the list of scheduled cron jobs. B