summaryrefslogtreecommitdiffstats
path: root/scripts/cron.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/cron.pl')
-rw-r--r--scripts/cron.pl306
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