App-Cronjob

 view release on metacpan or  search on metacpan

lib/App/Cronjob.pm  view on Meta::CPAN

  $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;
  }
}



( run in 1.718 second using v1.01-cache-2.11-cpan-5a3173703d6 )