diff options
Diffstat (limited to '')
-rw-r--r-- | scripts/cron.pl | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/scripts/cron.pl b/scripts/cron.pl new file mode 100644 index 0000000..0d1ac1e --- /dev/null +++ b/scripts/cron.pl @@ -0,0 +1,306 @@ +# +# Short help/usage: +# /jobadd hour minute day_of_month month day_of_week command +# Possibile switches for jobadd: +# -disabled +# -server <tag> +# -<number> +# /jobs [-v] +# /jobdel [-finished] | job_number +# /jobdisable job_number +# /jobenable job_number +# /jobssave +# /jobsload +# +# Examples of usage: +# /jobadd 17 45 * * * /echo This will be executed at 17:45 +# /jobadd -5 17 45 * * * /echo The same as above but only 5 times +# /jobadd * 05 * * * /echo Execute this every hour 5 minutes after the hour +# /jobadd */6 0 * * * /echo Execute at 0:0, 6:0, 12:0, 18:0 +# /jobadd * */30,45 * * * /echo Execute every hour at 00, 30, 45 minute +# /jobadd * 1-15/5 * * * /echo at 1,6,11 +# +# The servertag in -server usually is name from /ircnet, but +# should work with servers not in any ircnet (hmm probably) +# +# The format was taken from crontab(5). +# The only differences are: +# 1) hour field is before minute field (why the hell minute is first in +# crontab?). But this could be changed in final version. +# 2) day of week is 0..6. 0 is Sunday, 1 is Monday, 6 is Saturday. +# 7 is illegal value while in crontab it's the same as 0 (i.e. Sunday). +# I might change this, depends on demand. +# 3) you can't use names in month and day of week. You must use numbers +# Type 'man 5 crontab' to know more about allowed values etc. +# +# TODO: +# - add full (or almost full) cron functionality +# - probably more efficient checking for job in timeout +# - imput data validation +# ? should we remember if the server was given with -server +# +# Changelog: +# 0.12 (2014.11.12) +# Automatically load jobs when loaded +# +# 0.11 (2004.12.12) +# Job are executed exactly at the time (+- 1s), not up to 59s late +# +# 0.10 (2003.03.25): +# Added -<number> to execute job only <number> times. Initial patch from +# Marian Schubert (M dot Schubert at sh dot cvut dot cz) +# +# 0.9: +# Bugfix: according to crontab(5) when both DoM and DoW are restricted +# it's enough to only one of fields to match +# +# 0.8: +# Added -disabled to /jobadd +# Added jobs loading and saving to file +# +# 0.7: +# Bugfixes. Should work now ;) +# +# 0.6: +# Added month, day of month, day of week +# +# 0.5: +# Initial testing release +# + +use Irssi; +use strict; +use vars qw($VERSION %IRSSI); + +$VERSION = "0.12"; +%IRSSI = ( + authors => 'Piotr Krukowiecki', + contact => 'piotr \at/ krukowiecki /dot\ net', + name => 'cron aka jobs', + description => 'cron implementation, allows to execute commands at given interval/time', + license => 'GNU GPLv2', + changed => '2004.12.12', + url => 'http://www.krukowiecki.net/code/irssi/' +); + +my @jobs = (); +my $seconds = (gmtime(time()))[0]; +my $timeout_tag; +my $stop_timeout_tag; +if ($seconds > 0) { + $stop_timeout_tag = Irssi::timeout_add((60-$seconds)*1000, + sub { + Irssi::timeout_remove($stop_timeout_tag); + $timeout_tag = Irssi::timeout_add(60000, 'sig_timeout', undef); + }, undef); +} else { + $timeout_tag = Irssi::timeout_add(60000, 'sig_timeout', undef); +} +my $savefile = Irssi::get_irssi_dir() . "/cron.save"; + +# First arg - current hour or minute. +# Second arg - hour or minute specyfications. +sub time_matches($$) { + my ($current, $spec) = @_; + foreach my $h (split(/,/, $spec)) { + if ($h =~ /(.*)\/(\d+)/) { # */number or number-number/number + my $step = $2; + if ($1 eq '*') { # */number + return 1 if ($current % $step == 0); + next; + } + if ($1 =~ /(\d+)-(\d+)/) { # number-number/number + my ($from, $to) = ($1, $2); + next if ($current < $from or $current > $to); # not in range + my $current = $current; + if ($from > 0) { # shift time + $to -= $from; + $current -= $from; + $from = 0; + } + return 1 if ($current % $step == 0); + next; + } + next; + } + if ($h =~ /(\d+)-(\d+)/) { # number-number + return 1 if ($current >= $1 and $current <= $2); + next + } + return 1 if ($h eq '*' or $h == $current); # '*' or exact hour + } + return 0; +} + +sub sig_timeout { + my $ctime = time(); + my ($cminute, $chour, $cdom, $cmonth, $cdow) = (localtime($ctime))[1,2,3,4,6]; + $cmonth += 1; + foreach my $job (@jobs) { + next if ($job->{'disabled'}); + next if ($job->{'repeats'} == 0); + next if (not time_matches($chour, $job->{'hour'})); + next if (not time_matches($cminute, $job->{'minute'})); + next if (not time_matches($cmonth, $job->{'month'})); + if ($job->{'dom'} ne '*' and $job->{'dow'} ne '*') { + next if (not (time_matches($cdom, $job->{'dom'}) or + time_matches($cdow, $job->{'dow'}))); + } else { + next if (not time_matches($cdom, $job->{'dom'})); + next if (not time_matches($cdow, $job->{'dow'})); + } + + my $server = Irssi::server_find_tag($job->{'server'}); + if (!$server) { + Irssi::print("cron.pl: could not find server '$job->{server}'"); + next; + } + $server->command($job->{'commands'}); + if ($job->{'repeats'} > 0) { + $job->{'repeats'} -= 1; + } + } +} + +sub cmd_jobs { + my ($data, $server, $channel) = @_; + my $verbose = ($data eq '-v'); + Irssi::print("Current Jobs:"); + foreach (0 .. $#jobs) { + my $repeats = $jobs[$_]{'repeats'}; + my $msg = "$_) "; + if (!$verbose) { + next if ($repeats == 0); + $msg .= "-$repeats " if ($repeats != -1); + } else { + $msg .= "-$repeats " if ($repeats != -1); + } + + $msg .= ($jobs[$_]{'disabled'}?"-disabled ":"") + ."-server $jobs[$_]{server} " + ."$jobs[$_]{hour} $jobs[$_]{minute} $jobs[$_]{dom} " + ."$jobs[$_]{month} $jobs[$_]{dow} " + ."$jobs[$_]{commands}"; + Irssi::print($msg); + } + Irssi::print("End of List"); +} + +# /jobdel job_number +sub cmd_jobdel { + my ($data, $server, $channel) = @_; + if ($data eq "-finished") { + foreach (reverse(0 .. $#jobs)) { + if ($jobs[$_]{'repeats'} == 0) { + splice(@jobs, $_, 1); + Irssi::print("Removed Job #$_"); + } + } + return; + } elsif ($data !~ /\d+/ or $data < 0 or $data > $#jobs) { + Irssi::print("Bad Job Number"); + return; + } + splice(@jobs, $data, 1); + Irssi::print("Removed Job #$data"); +} + +# /jobdisable job_number +sub cmd_jobdisable { + my ($data, $server, $channel) = @_; + if ($data < 0 || $data > $#jobs) { + Irssi::print("Bad Job Number"); + return; + } + $jobs[$data]{'disabled'} = 1; + Irssi::print("Disabled job number $data"); +} +# /jobenable job_number +sub cmd_jobenable { + my ($data, $server, $channel) = @_; + if ($data < 0 || $data > $#jobs) { + Irssi::print("Bad Job Number"); + return; + } + $jobs[$data]{'disabled'} = 0; + Irssi::print("Enabled job number $data"); +} + +# /jobadd [-X] [-disabled] [-server servertag] hour minute day_of_month month day_of_week command +sub cmd_jobadd { + my ($data, $server, $channel) = @_; + + $server = $server->{tag}; + my $disabled = 0; + my $repeats = -1; + while ($data =~ /^\s*-/) { + if ($data =~ s/^\s*-disabled\s+//) { + $disabled = 1; + next; + } + if ($data =~ s/^\s*-(\d+)\s+//) { + $repeats = $1; + next; + } + my $comm; + ($comm, $server, $data) = split(' ', $data, 3); + if ($comm ne '-server') { + Irssi::print("Bad switch: '$comm'"); + return; + } + } + my ($hour, $minute, $dom, $month, $dow, $commands) = split(' ', $data, 6); + + push (@jobs, { 'hour' => $hour, 'minute' => $minute, 'dom' => $dom, + 'month' => $month, 'dow' => $dow, + 'server' => $server, 'commands' => $commands, + 'disabled' => $disabled, 'repeats' => $repeats } ); + Irssi::print("Job added"); +} + +sub cmd_jobssave { + if (not open (FILE, ">", $savefile)) { + Irssi::print("Could not open file '$savefile': $!"); + return; + } + foreach (0 .. $#jobs) { + next if ($jobs[$_]->{'repeats'} == 0); # don't save finished jobs + print FILE + ($jobs[$_]->{'repeats'}>0 ? "-$jobs[$_]->{'repeats'} " : "") + . ($jobs[$_]{'disabled'}?"-disabled ":"") + ."-server $jobs[$_]{server} " + ."$jobs[$_]{hour} $jobs[$_]{minute} $jobs[$_]{dom} " + ."$jobs[$_]{month} $jobs[$_]{dow} " + ."$jobs[$_]{commands}\n"; + } + close FILE; + Irssi::print("Jobs saved"); +} + +sub cmd_jobsload { + if (not open (FILE, q{<}, $savefile)) { + Irssi::print("Could not open file '$savefile': $!"); + return; + } + @jobs = (); + + while (<FILE>) { + chomp; + cmd_jobadd($_, undef, undef); + } + + close FILE; + Irssi::print("Jobs loaded"); +} + +cmd_jobsload(); + +Irssi::command_bind('jobs', 'cmd_jobs', 'Cron'); +Irssi::command_bind('jobadd', 'cmd_jobadd', 'Cron'); +Irssi::command_bind('jobdel', 'cmd_jobdel', 'Cron'); +Irssi::command_bind('jobdisable', 'cmd_jobdisable', 'Cron'); +Irssi::command_bind('jobenable', 'cmd_jobenable', 'Cron'); +Irssi::command_bind('jobssave', 'cmd_jobssave', 'Cron'); +Irssi::command_bind('jobsload', 'cmd_jobsload', 'Cron'); + +# vim:noexpandtab:ts=4 |