App-Cronjob
view release on metacpan or search on metacpan
lib/App/Cronjob.pm view on Meta::CPAN
$subject =~ s{\A/\S+/([^/]+)(\s|$)}{$1$2} if $subject eq $opt->{command};
if (defined $opt->{temp_ignore_lock_errors}) {
if (grep {; $_ eq "lock" } @{$opt->{ignore_errors}}) {
die "--temp-ignore-lock-errors and --ignore-errors=lock are incompatible\n";
}
}
my @extra_headers;
for my $entry (@{ $opt->email_header || [] }) {
my ($h, $v) = split /=/, $entry;
lc $h eq $_ && die "You can't replace the $_ header!\n"
for qw(to from subject);
die "Invalid header name: $h" if $h =~ /\P{ASCII}/;
die "Invalid header name: $h" unless $h =~ /\A[A-Za-z][-A-Za-z0-9]*\z/;
push @extra_headers, $h, $v;
}
$rcpts = $opt->{rcpt}
|| [ split /\s*,\s*/, ($ENV{MAILTO} ? $ENV{MAILTO} : 'root') ];
$host = hostname_long;
$sender = $opt->{sender} || sprintf '%s@%s', ($ENV{USER}||'cron'), $host;
my $got_lock;
my $okay = eval {
die "illegal job name: $opt->{jobname}\n"
if $opt->{jobname} and $opt->{jobname} !~ m{\A[-_A-Za-z0-9]+\z};
my $job_id = $opt->{jobname} || md5_hex($subject);
my $lockfile = sprintf '%s/cronjob.%s',
$ENV{APP_CRONJOB_LOCKDIR} || '/tmp',
$job_id;
my $logger = Log::Dispatchouli->new({
ident => 'cronjob',
facility => 'cron',
log_pid => 1,
prefix => "$job_id: ",
});
my $lock_fh;
if ($opt->lock) {
sysopen $lock_fh, $lockfile, O_CREAT|O_WRONLY
or die App::Cronjob::Exception->new(
lockfile => "couldn't open lockfile $lockfile: $!"
);
my $lock_flags = LOCK_EX | LOCK_NB;
unless (flock $lock_fh, $lock_flags) {
my $error = $!;
my $mtime = (stat $lock_fh)[9];
my $stamp = scalar localtime $mtime;
die App::Cronjob::Exception->new(
lock => "can't lock; $!; lockfile created $stamp",
{ locked_since => $mtime },
);
}
printf $lock_fh "pid %s running %s\nstarted at %s\n",
$$, $opt->{command}, scalar localtime $^T;
$got_lock = 1;
}
$logger->log([ 'trying to run %s', $opt->{command} ]);
my $start = Time::HiRes::time;
my $output;
my $ok = eval {
local $SIG{ALRM} = sub { die "command took too long to run" };
alarm($opt->timeout) if $opt->timeout;
run3($opt->{command}, \undef, \$output, \$output);
alarm(0) if $opt->timeout;
1;
};
unless ($ok) {
# XXX: does not throw proper exception
$logger->log_fatal([ 'run3 failed to run command: %s', $@ ]);
}
my $status = Process::Status->new;
my $end = Time::HiRes::time;
my $send_mail = ($status->exitstatus != 0)
|| (length $output && ! $opt->{errors_only});
my $time_taken = sprintf '%0.4f', $end - $start;
$logger->log([
'job completed with status %s after %ss',
$status->as_struct,
$time_taken,
]);
if ($send_mail) {
send_cronjob_report({
is_fail => (!! $status->exitstatus),
status => $status,
time => \$time_taken,
output => \$output,
extra_headers => \@extra_headers,
});
}
1;
};
exit 0 if $okay;
my $err = $@;
if (eval { $err->isa('App::Cronjob::Exception'); }) {
unless (
grep { $err->{type} and $_ eq $err->{type} } @{$opt->{ignore_errors}}
) {
if ($err->{type} eq "lock" && $opt->{temp_ignore_lock_errors}) {
my $age = time() - $err->{extra}{locked_since};
exit 0 if $age <= $opt->{temp_ignore_lock_errors};
}
send_cronjob_report({
is_fail => 1,
output => \$err->{text},
extra_headers => \@extra_headers,
});
}
exit 0;
} else {
$subject = "ERROR: $subject";
send_cronjob_report({
is_fail => 1,
output => \$err,
extra_headers => \@extra_headers,
});
exit 0;
}
}
# read INI from /etc/cronjob
#sub __config {
#}
sub send_cronjob_report {
my ($arg) = @_;
require Email::Simple;
require Email::Simple::Creator;
require Email::Sender::Simple;
require Text::Template;
my $body = Text::Template->fill_this_in(
$TEMPLATE,
HASH => {
command => \$opt->{command},
output => $arg->{output},
time => $arg->{time} || \'(n/a)',
status => \($arg->{status} ? $arg->{status}->as_string : 'never ran'),
},
);
my $subject = sprintf '%s%s', ($arg->{is_fail} ? 'FAIL: ' : ''), $subject;
my $irt = sprintf '<%s@%s>', md5_hex($subject), $host;
my $email = Email::Simple->create(
body => $body,
header => [
To => join(', ', @$rcpts),
From => qq{"cron/$host" <$sender>},
Subject => $subject,
'In-Reply-To' => $irt,
'Auto-Submitted' => 'auto-generated',
@{ $arg->{extra_headers} },
],
);
Email::Sender::Simple->send(
( run in 1.505 second using v1.01-cache-2.11-cpan-744e820c463 )