Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/perl
- use warnings;
- use strict;
- # You are free to use this software under the terms of a BSD-style license
- # Copyright TurdHurdur(pseudonym) of forum.bitcoin.org
- use POE qw(Wheel::Run Filter::Stream);
- use File::Basename qw(dirname);
- use Cwd qw(getcwd);
- use JSON;
- use Getopt::Long qw(GetOptions);
- use Pod::Usage;
- pod2usage(1) if (!@ARGV);
- my ($arg_file, $arg_help) = ('./aids_conf.json', undef);
- GetOptions(
- 'f|file:s' => \$arg_file,
- 'h|help' => \$arg_help
- ) or pod2usage(1);
- pod2usage(1) if ($arg_help);
- my %default_penalty_trigger_keys = (
- 'penalty' => 0,
- 'triggers_switch' => 1,
- 'repeat_penalty' => 0,
- 'stuck_penalty' => 0
- );
- open my $fh, '<', $arg_file;
- my $string = do { local $/ = <$fh> };
- close $fh;
- $string =~ s/\/\/#.+//g;
- my $config = decode_json $string;
- for (keys %{$config->{connections}}) {
- $config->{connections}{$_}{command} = argify($config->{connections}{$_}{command});
- }
- sub argify {
- my $args = shift;
- if (index($args, '"') == 0) {
- $args = [split(/"/, $args, 3)];
- shift(@$args);
- $args->[0] =~ s/(?<!\\) /\\ /g;
- my @args_args = split(/ +/, $args->[1]);
- shift(@args_args);
- @$args[1..@args_args] = @args_args;
- } else {
- $args = [split(/(?<!\\) +/, $args)];
- }
- $args->[0] =~ s/\\(?! )/\//g;
- return $args;
- }
- $SIG{$_} = \&catch_sigs for(qw(INT TERM KILL));
- POE::Session->create(
- inline_states => {
- _start => \&on_start,
- got_child_stdout => \&on_child_stdout,
- got_child_stderr => \&on_child_stderr,
- got_child_close => \&on_child_close,
- got_child_signal => \&on_child_signal,
- close_all => \&close_all,
- run_new => \&run_new,
- error_state => \&error_state,
- handle_switch => \&handle_switch,
- granularity_tick => \&granularity_tick
- }
- );
- $poe_kernel->run();
- exit 0;
- sub catch_sigs {
- my $signame = shift;
- # print "\n\nCaught SIG$signame\n" if ($arg_verbose > 0);
- $poe_kernel->call('s_alias', 'close_all', $signame);
- }
- sub close_all {
- $poe_kernel->alarm_remove_all();
- for my $key (keys %{$_[HEAP]{children_by_wid}}) {
- $_[HEAP]{children_by_wid}{$key}->put("\cC");
- $_[HEAP]{children_by_wid}{$key}->kill('INT');
- #$_[HEAP]{children_by_wid}{$key}->kill('TERM');
- #$_[HEAP]{children_by_wid}{$key}->kill('KILL');
- }
- exit;
- }
- sub granularity_tick {
- my ($heap, $tick) = @_[HEAP, ARG0];
- my $runtime = $config->{interval_granularity} * $tick;
- $poe_kernel->delay_add('granularity_tick' => $config->{interval_granularity}, ++$tick);
- if (defined($config->{intervals_seconds}{rem_penalty})) {
- $heap->{intervals_status}{rem_penalty} -= $config->{interval_granularity};
- if ($heap->{intervals_status}{rem_penalty} <= 0) {
- for my $k (keys %{$config->{connections}}) {
- $heap->{miner_status}{$k}{penalty} -= $config->{connections}{$k}{rem_penalty_interval_amt};
- $heap->{miner_status}{$k}{penalty} = $heap->{miner_status}{$k}{penalty} < 0 ? 0 : $heap->{miner_status}{$k}{penalty};
- }
- $heap->{intervals_status}{rem_penalty} = $config->{intervals_seconds}{rem_penalty};
- }
- }
- my $live_conn_name = (grep {$heap->{miner_status}{$_}{live}} keys %{$heap->{miner_status}})[0];
- if (!$live_conn_name) {
- warn 'Unusual occurance no $live_conn_name';
- return;
- }
- if (defined($config->{intervals_seconds}{stuck_check})) {
- $heap->{intervals_status}{stuck_check} -= $config->{interval_granularity};
- if ($heap->{intervals_status}{stuck_check} <= 0) {
- $heap->{intervals_status}{stuck_check} = $config->{intervals_seconds}{stuck_check};
- if ($heap->{miner_status}{$live_conn_name}{lastline_time} < time() - $config->{intervals_seconds}{stuck_check}) {
- for my $penalty_key (keys %{$config->{penalty_triggers}}) {
- if (
- $config->{penalty_triggers}{$penalty_key}{stuck_switch} &&
- (!defined($config->{connections}{$live_conn_name}{applicable_triggers}) ||
- grep {$penalty_key eq $_} $config->{connections}{$live_conn_name}{applicable_triggers}) &&
- $heap->{miner_status}{$live_conn_name}{lastline_time} != $heap->{miner_status}{$live_conn_name}{firstline_time} &&
- $heap->{miner_status}{$live_conn_name}{lastline} =~ /$config->{penalty_triggers}{$penalty_key}{regex}/
- ) {
- $heap->{miner_status}{$live_conn_name}{penalty} += $config->{penalty_triggers}{$penalty_key}{stuck_penalty};
- print 'Stuck penalty applied at line ' . __LINE__ . "\n";
- print "\nSwitch at " . __LINE__ . "\n";
- $poe_kernel->post('s_alias', 'handle_switch', $live_conn_name);
- return;
- }
- }
- }
- }
- }
- if (defined($config->{intervals_seconds}{switch_check})) {
- $heap->{intervals_status}{switch_check} -= $config->{interval_granularity};
- if ($heap->{intervals_status}{switch_check} <= 0) {
- $heap->{intervals_status}{switch_check} = $config->{intervals_seconds}{switch_check};
- if ((sort {$config->{connections}{$a}{base_priority} + $heap->{miner_status}{$a}{penalty} <=> $config->{connections}{$b}{base_priority} + $heap->{miner_status}{$b}{penalty}} keys %{$config->{connections}})[0] ne $live_conn_name) {
- print "\nSwitch at " . __LINE__ . "\n";
- $poe_kernel->post('s_alias', 'handle_switch', $live_conn_name);
- return;
- }
- }
- }
- if (defined($heap->{miner_status}{$live_conn_name}{switch_countdown})) {
- $heap->{miner_status}{$live_conn_name}{switch_countdown} -= $config->{interval_granularity};
- if ($heap->{miner_status}{$live_conn_name}{switch_countdown} <= 0) {
- $heap->{miner_status}{$live_conn_name}{penalty} += $config->{connections}{$live_conn_name}{switch_after_sec_penalty};
- $heap->{miner_status}{$live_conn_name}{switch_countdown} = $config->{connections}{$live_conn_name}{switch_after_sec};
- print "\nSwitch at " . __LINE__ . "\n";
- $poe_kernel->post('s_alias', 'handle_switch', $live_conn_name);
- }
- }
- }
- sub run_new {
- my ($wheelname, $command) = @_[ARG0, ARG1];
- return if ($_[HEAP]{miner_status}{$wheelname}{live});
- print "\nStarting $wheelname\n";
- %{$_[HEAP]{intervals_status}} = %{$config->{intervals_seconds}} if (!defined($_[HEAP]{intervals_status}));
- my $old_dir = getcwd;
- chdir dirname($command->[0]);
- $_[HEAP]{miner}{$wheelname} = POE::Wheel::Run->new(
- Program => $command,
- Conduit => $config->{connections}{$wheelname}{conduit} || undef,
- StdoutFilter => $config->{connections}{$wheelname}{nostream} ? POE::Filter::Line->new(InputRegexp => qr/\s*[\r\n]{1,2}\s*/, OutputLiteral => "\r\n") : POE::Filter::Stream->new(),
- StderrFilter => $config->{connections}{$wheelname}{nostream} ? POE::Filter::Line->new(InputRegexp => qr/\s*[\r\n]{1,2}\s*/, OutputLiteral => "\r\n") : POE::Filter::Stream->new(),
- StdoutEvent => "got_child_stdout",
- StderrEvent => "got_child_stderr",
- CloseEvent => "got_child_close",
- ErrorEvent => "error_state"
- ) or die "$0: can't POE::Wheel::Run->new";
- chdir $old_dir;
- $_[KERNEL]->sig_child($_[HEAP]{miner}{$wheelname}->PID, "got_child_signal");
- $_[HEAP]{children_by_wid}{$_[HEAP]{miner}{$wheelname}->ID} = $_[HEAP]{miner}{$wheelname};
- $_[HEAP]{children_by_pid}{$_[HEAP]{miner}{$wheelname}->PID} = $_[HEAP]{miner}{$wheelname};
- $_[HEAP]{miner_status}{$wheelname}{live} = 1;
- $_[HEAP]{miner_status}{$wheelname}{penalty} = 0 if (!defined($_[HEAP]{miner_status}{$wheelname}{penalty}));
- $_[HEAP]{miner_status}{$wheelname}{lastline} = '';
- $_[HEAP]{miner_status}{$wheelname}{lastline_time} = 0;
- $_[HEAP]{miner_status}{$wheelname}{firstline_time} = 0;
- if (!defined($_[HEAP]{miner_status}{$wheelname}{switch_countdown}) && defined($config->{connections}{$wheelname}{switch_after_sec})) {
- $_[HEAP]{miner_status}{$wheelname}{switch_countdown} = $config->{connections}{$wheelname}{switch_after_sec};
- }
- # $_[HEAP]{miner_status}{$wheelname}{pid} = $_[HEAP]{miner}{$wheelname}->PID;
- #print(
- # "\nChild pid ", $_[HEAP]{miner}{$wheelname}->PID,
- # " started as wheel ", $_[HEAP]{miner}{$wheelname}->ID, ".\n"
- #) if ($arg_verbose > 0);
- return $_[HEAP]{miner}{$wheelname};
- }
- sub handle_switch {
- my ($heap, $conn_key) = @_[HEAP, ARG0];
- $heap->{miner_status}{$conn_key}{lastline_time} = $heap->{miner_status}{$conn_key}{firstline_time};
- $heap->{miner}{$conn_key}->put("\cC");
- $heap->{miner}{$conn_key}->kill('INT');
- my $next_up;
- my @other_conns = grep({$_ ne $conn_key} keys %{$config->{connections}});
- if (@other_conns > 1) {
- $next_up = (sort({$config->{connections}{$a}{base_priority} + $heap->{miner_status}{$a}{penalty} <=> $config->{connections}{$b}{base_priority} + $heap->{miner_status}{$b}{penalty}} @other_conns))[0];
- } else {
- $next_up = $other_conns[0];
- }
- print "\nStopping $conn_key\n";
- $poe_kernel->post('s_alias', 'run_new', $next_up, $config->{connections}{$next_up}{command});
- }
- sub error_state {
- my ($operation, $errnum, $errstr, $wheel_id) = @_[ARG0..ARG3];
- $errstr = "\nremote end closed" if $operation eq "read" and !$errnum;
- #warn "\n\nWheel $wheel_id generated $operation error $errnum: $errstr\n" if ($arg_verbose > 0);
- }
- sub on_start {
- $_[KERNEL]->alias_set('s_alias');
- for my $k (keys %{$config->{connections}}) {
- $_[HEAP]{miner_status}{$k}{penalty} = 0;
- }
- $poe_kernel->delay_add('granularity_tick' => $config->{interval_granularity}, 1);
- for my $k (keys %{$config->{penalty_triggers}}) {
- for (keys %default_penalty_trigger_keys) {
- $config->{penalty_triggers}{$k}{$_} = defined($config->{penalty_triggers}{$k}{$_}) ? $config->{penalty_triggers}{$k}{$_} : $default_penalty_trigger_keys{${_}};
- }
- }
- my $first_miner = (sort {$config->{connections}{$a}{base_priority} <=> $config->{connections}{$b}{base_priority}} keys %{$config->{connections}})[0];
- $_[KERNEL]->post('s_alias', 'run_new', $first_miner, $config->{connections}{$first_miner}{command});
- }
- sub on_child_stdout {
- on_child_stdout_or_err('stdout', @_);
- }
- sub on_child_stderr {
- on_child_stdout_or_err('stderr', @_);
- }
- sub on_child_stdout_or_err {
- my $from = shift;
- my ($heap, $line, $wheel_id) = @_[HEAP, ARG0, ARG1];
- for my $conn_key (keys %{$config->{connections}}) {
- if (defined($heap->{miner}{$conn_key}) && $wheel_id == $heap->{miner}{$conn_key}->ID) {
- for my $penalty_key (keys %{$config->{penalty_triggers}}) {
- if (
- (!defined($config->{connections}{$conn_key}{applicable_triggers}) ||
- grep {$penalty_key eq $_} @{$config->{connections}{$conn_key}{applicable_triggers}}) &&
- $line =~ /$config->{penalty_triggers}{$penalty_key}{regex}/
- ) {
- my $time = time();
- if ($line eq $heap->{miner_status}{$conn_key}{lastline}) {
- $heap->{miner_status}{$conn_key}{penalty} += $config->{penalty_triggers}{$penalty_key}{repeat_penalty};
- if ($config->{penalty_triggers}{$penalty_key}{repeat_switch}) {
- $poe_kernel->post('s_alias', 'handle_switch', $conn_key);
- print "\nSwitch at " . __LINE__ . "\n";
- last;
- }
- }
- $heap->{miner_status}{$conn_key}{penalty} += $config->{penalty_triggers}{$penalty_key}{penalty};
- $heap->{miner_status}{$conn_key}{lastline} = $line;
- $heap->{miner_status}{$conn_key}{lastline_time} = $time;
- $heap->{miner_status}{$conn_key}{firstline_time} = $time if (!$heap->{miner_status}{$conn_key}{firstline_time});
- if ($config->{penalty_triggers}{$penalty_key}{triggers_switch}) {
- $poe_kernel->post('s_alias', 'handle_switch', $conn_key);
- print "\nSwitch at " . __LINE__ . "\n";
- last;
- }
- }
- }
- $line =~ s/([\x08\s])/$1/g;
- #$line = $arg_verbose > 0 && $from eq 'stderr' ? $line . ' from stderr' : $line;
- if (!defined($config->{connections}{$conn_key}{nostream}))
- {
- local $| = 1; # enable autoflush
- print $line;
- } else {
- $line =~ s/[\r\n]//g;
- print $line . "\n";
- }
- return;
- }
- }
- }
- sub on_child_close {
- my $wheel_id = $_[ARG0];
- my $child = delete $_[HEAP]{children_by_wid}{$wheel_id};
- unless (defined $child) {
- # print "\nwid $wheel_id closed all pipes.\n" if ($arg_verbose > 0);
- return;
- }
- # print "\npid ", $child->PID, " closed all pipes.\n" if ($arg_verbose > 0);
- delete $_[HEAP]{children_by_pid}{$child->PID};
- }
- sub on_child_signal {
- # print "\npid $_[ARG1] exited with status $_[ARG2].\n" if ($arg_verbose > 0);
- my $child = delete $_[HEAP]{children_by_pid}{$_[ARG1]};
- for my $miner (keys %{$_[HEAP]{miner}}) {
- if ($_[HEAP]{miner}{$miner}->PID == $_[ARG1]) {
- $_[HEAP]{miner_status}{$miner}{live} = 0;
- }
- }
- return unless defined $child;
- delete $_[HEAP]{children_by_wid}{$child->ID};
- }
- __END__
- =head1 NAME
- aids.pl - Using aids.pl
- =head1 SYNOPSIS
- aids.pl [-f filename]
- Options:
- --file -f config file to use
- --help -h this help message
- =cut
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement