Changeset 5 for shepherd

Show
Ignore:
Timestamp:
10/04/06 00:23:03 (7 years ago)
Author:
lincoln
Message:

postprocessor support

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • shepherd

    r4 r5  
    33# "Shepherd" 
    44 
    5 my $version = '0.2.5ng'; 
     5my $version = '0.2.6'; 
    66 
    77# A wrapper for various Aussie TV guide data grabbers 
     
    1919# 0.2.2   : --check 
    2020# 0.2.3   : Bugfix: archives correctly 
    21 # 0.2.5ng : multi-grabber (potentially with partial data) & postprocessor support 
     21# 0.2.5   : multi-grabber (potentially with partial data) 
     22# 0.2.6   : postprocessor support 
    2223# 
    2324# ToDo: 
     
    2829#   --desc or --ready 
    2930# * --ready  option that says whether config is required? 
    30 # * "Last Data" column in status 
    3131 
    3232use strict; 
     
    3939use XMLTV::Ask; 
    4040use DateTime::Format::Strptime; 
     41use POSIX qw(strftime); 
    4142 
    4243# --------------------------------------------------------------------------- 
     
    5859 
    5960my $GRABBER_DIR = "$CWD/grabbers"; 
     61my $POSTPROCESSOR_DIR = "$CWD/postprocessors"; 
    6062my $ARCHIVE_DIR = "$CWD/archive"; 
    61 my $timeslot_size = (15 * 60); # 15 minute slots 
    62 my $channel_ok_threshold_percent = 90; 
     63 
     64my $timeslot_size = (15 * 60);                  # 15 minute slots 
     65my $channel_ok_threshold_percent = 90;          # 90% these may need to be tweaked but look ok for now 
     66my $postprocessor_ok_threshold_percent = 80;    # 80% these may need to be tweaked but look ok for now 
     67my $postprocessor_disable_failure_threshold = 5; # number of times a postprocessor has to fail in a row before it is automatically disabled 
    6368 
    6469my $opt; 
     70my $pref_order; 
     71my $made_changes = 0; 
    6572my $debug = 1; 
    6673my $grabbers = { }; 
     74my $postprocessors = { }; 
    6775my $preferred; # obsolete but may still exist in shepherd.conf 
    6876my $region; 
     
    7583my $langs = [ 'en' ]; 
    7684my $num_timeslots; 
    77 my $grabber_data = { }; 
     85my $plugin_data = { }; 
    7886my $channel_data = { }; 
    7987my $starttime, my $endtime; 
     88my $input_postprocess_files = ""; 
     89my $insufficient_grabber_data = 0; 
    8090 
    8191# --------------------------------------------------------------------------- 
     
    123133} 
    124134 
    125 &set_order($opt->{setorder}) if ($opt->{setorder}); 
    126  
    127 if ($opt->{check}) 
    128 { 
    129     check(); 
    130 } 
     135&set_order(0,$opt->{setorder}) if ($opt->{setorder}); 
     136&check() if ($opt->{check}); 
    131137 
    132138if ($opt->{enable} or $opt->{disable} or $opt->{setorder} or $opt->{check}) 
    133139{ 
    134     write_config_file(); 
     140    set_order(1) if $made_changes; 
     141    write_config_file() if $made_changes; 
    135142    status(); 
    136143    exit; 
     
    160167    calc_date_range(); 
    161168    grab_data(); 
     169    postprocess_data(); 
     170    output_data(); 
    162171} 
    163172 
     
    165174 
    166175status(); 
     176write_config_file(); 
    167177 
    168178# --------------------------------------------------------------------------- 
     
    184194        $used_grabbers++; 
    185195 
    186         $grabber_data->{$grabber}->{last_grabbed} = time; 
     196        $grabbers->{$grabber}->{lastdata} = time; 
     197        $grabbers->{$grabber}->{laststatus} = "unknown"; 
    187198 
    188199        printf "SHEPHERD: Using grabber: (%d) %s\n",$grabbers->{$grabber}->{order},$grabber; 
    189200 
    190201        my $output = "$GRABBER_DIR/$grabber/output.xmltv"; 
     202        $input_postprocess_files .= "$output "; 
     203 
    191204        my $comm = "$GRABBER_DIR/$grabber/$grabber " . 
    192205                   "--region $region " . 
     
    208221 
    209222        # soak up the data we just collected 
    210         &soak_up_grabber_data($grabber, $output); 
     223        &soak_up_data($grabber, $output); 
     224        $grabbers->{$grabber}->{laststatus} = $plugin_data->{$grabber}->{laststatus}; 
    211225 
    212226        # check to see if we have all the data we want 
    213         $need_more_data = &analyze_grabber_data(); 
     227        $need_more_data = &analyze_plugin_data($channel_ok_threshold_percent); 
    214228 
    215229        last if (!$need_more_data); 
     
    226240    { 
    227241        print "SHEPHERD: Ran through ALL grabbers but still missing data!!! :(\n"; 
     242        $insufficient_grabber_data = 1; 
    228243        return; 
    229244    } 
     
    231246 
    232247 
    233 # interpret xmltv data from this grabber 
    234 sub soak_up_grabber_data 
     248# interpret xmltv data from this grabber/postprocessor 
     249sub soak_up_data 
    235250{ 
    236251    my ($grabber, $output) = @_; 
    237     eval { $grabber_data->{$grabber}->{xmltv} = XMLTV::parsefiles($output); }; 
    238  
    239     if (defined $grabber_data->{$grabber}->{xmltv}) { 
    240         $grabber_data->{$grabber}->{valid} = 1; 
    241  
    242         my $xmltv = $grabber_data->{$grabber}->{xmltv}; 
     252    eval { $plugin_data->{$grabber}->{xmltv} = XMLTV::parsefiles($output); }; 
     253 
     254    if (defined $plugin_data->{$grabber}->{xmltv}) { 
     255        $plugin_data->{$grabber}->{valid} = 1; 
     256 
     257        my $xmltv = $plugin_data->{$grabber}->{xmltv}; 
    243258        my ($encoding, $credits, $chan, $progs) = @$xmltv; 
    244         $grabber_data->{$grabber}->{total_duration} = 0; 
    245         $grabber_data->{$grabber}->{programmes} = 0; 
     259        $plugin_data->{$grabber}->{total_duration} = 0; 
     260        $plugin_data->{$grabber}->{programmes} = 0; 
    246261 
    247262        my $strptime = new DateTime::Format::Strptime( pattern => "%Y%m%d%H%M %z"); 
     
    261276 
    262277                # store grabber-specific stats 
    263                 $grabber_data->{$grabber}->{programmes}++; 
    264                 $grabber_data->{$grabber}->{total_duration} += ($t2->epoch - $t1->epoch); 
     278                $plugin_data->{$grabber}->{programmes}++; 
     279                $plugin_data->{$grabber}->{total_duration} += ($t2->epoch - $t1->epoch); 
    265280                $seen_progs_on_this_channel++; 
    266                 $grabber_data->{$grabber}->{earliest_data_seen} = $t1->epoch if (!defined $grabber_data->{$grabber}->{earliest_data_seen}); 
    267                 $grabber_data->{$grabber}->{earliest_data_seen} = $t1->epoch if ($t1->epoch < $grabber_data->{$grabber}->{earliest_data_seen}); 
    268                 $grabber_data->{$grabber}->{latest_data_seen} = $t2->epoch if (!defined $grabber_data->{$grabber}->{latest_data_seen}); 
    269                 $grabber_data->{$grabber}->{latest_data_seen} = $t2->epoch if ($t2->epoch > $grabber_data->{$grabber}->{latest_data_seen}); 
     281                $plugin_data->{$grabber}->{earliest_data_seen} = $t1->epoch if (!defined $plugin_data->{$grabber}->{earliest_data_seen}); 
     282                $plugin_data->{$grabber}->{earliest_data_seen} = $t1->epoch if ($t1->epoch < $plugin_data->{$grabber}->{earliest_data_seen}); 
     283                $plugin_data->{$grabber}->{latest_data_seen} = $t2->epoch if (!defined $plugin_data->{$grabber}->{latest_data_seen}); 
     284                $plugin_data->{$grabber}->{latest_data_seen} = $t2->epoch if ($t2->epoch > $plugin_data->{$grabber}->{latest_data_seen}); 
    270285 
    271286                # store channel-specific stats 
     
    299314 
    300315        # print some stats about what we saw! 
    301         my $earlist_data_seen = localtime($grabber_data->{$grabber}->{earliest_data_seen}); 
    302         my $latest_data_seen = localtime($grabber_data->{$grabber}->{latest_data_seen}); 
    303         printf "SHEPHERD: Grabber '%s' returned data for %d channels (%d programmes, %dd%02dh%02dm%02ds duration, earliest %s, latest %s)\n", 
    304             $grabber, $seen_channels_with_data, $grabber_data->{$grabber}->{programmes}, 
    305             int($grabber_data->{$grabber}->{total_duration} / 86400),           # days 
    306             int(($grabber_data->{$grabber}->{total_duration} % 86400) / 3600),  # hours 
    307             int(($grabber_data->{$grabber}->{total_duration} % 3600) / 60),     # mins 
    308             int($grabber_data->{$grabber}->{total_duration} % 60),              # sec 
    309             $earlist_data_seen, $latest_data_seen; 
     316        printf "SHEPHERD: Grabber '%s' returned data for %d channels, %d programmes, %dd%02dh%02dm%02ds duration, %s%s\n", 
     317            $grabber, $seen_channels_with_data, $plugin_data->{$grabber}->{programmes}, 
     318            int($plugin_data->{$grabber}->{total_duration} / 86400),            # days 
     319            int(($plugin_data->{$grabber}->{total_duration} % 86400) / 3600),   # hours 
     320            int(($plugin_data->{$grabber}->{total_duration} % 3600) / 60),      # mins 
     321            int($plugin_data->{$grabber}->{total_duration} % 60),               # sec 
     322            (defined $plugin_data->{$grabber}->{earliest_data_seen} ? (strftime "%a %e %b %H:%M - ", localtime($plugin_data->{$grabber}->{earliest_data_seen})) : 'no data'), 
     323            (defined $plugin_data->{$grabber}->{latest_data_seen} ? (strftime "%a %e %b %H:%M", localtime($plugin_data->{$grabber}->{latest_data_seen})) : ''); 
     324        $plugin_data->{$grabber}->{laststatus} = sprintf "%dch/%dpr/%dhrs %s-%s", 
     325            $seen_channels_with_data, $plugin_data->{$grabber}->{programmes}, 
     326            int($plugin_data->{$grabber}->{total_duration} / 3600), 
     327            (defined $plugin_data->{$grabber}->{earliest_data_seen} ? (strftime "%a%d%b%H:%M", localtime($plugin_data->{$grabber}->{earliest_data_seen})) : 'no'), 
     328            (defined $plugin_data->{$grabber}->{latest_data_seen} ? (strftime "%a%d%b%H:%M", localtime($plugin_data->{$grabber}->{latest_data_seen})) : 'data'); 
     329 
    310330    } else { 
    311         printf "WARNING: Grabber %s didn't seem to return any valid XMLTV!\n",$grabber; 
    312         delete $grabber_data->{$grabber}->{valid}; 
     331        printf "WARNING: Plugin %s didn't seem to return any valid XMLTV!\n",$grabber; 
     332        delete $plugin_data->{$grabber}->{valid}; 
    313333    } 
    314334} 
     
    317337# analyze grabber data - do we have all the data we want? 
    318338# returns 1 if we need more data, 0 if we have all we want 
    319 sub analyze_grabber_data 
    320 { 
     339sub analyze_plugin_data 
     340{ 
     341    my $threshold = shift; 
    321342    my $retval = 0; # until proven otherwise 
    322343    my $total_data_percent = 0, my $total_channels = 0; 
     
    329350            my $data_in_channel = 0; 
    330351            for my $slotnum (0..($num_timeslots-1)) { 
    331                 $data_in_channel++ if ($channel_data->{$ch}->{timeslots}[$slotnum] > 0); 
     352                $data_in_channel++ if ((defined $channel_data->{$ch}->{timeslots}[$slotnum]) && ($channel_data->{$ch}->{timeslots}[$slotnum] > 0)); 
    332353            } 
    333354 
    334355            # do we have enough data for this channel? 
    335356            my $data_in_channel_percent = $data_in_channel / ($num_timeslots-1) * 100; 
    336             if ($data_in_channel_percent >= $channel_ok_threshold_percent) { 
     357            if ($data_in_channel_percent >= $threshold) { 
    337358                $statusstring .= sprintf "%s: %0.1f%% [complete], ",$ch,$data_in_channel_percent; 
    338359            } else { 
     
    381402 
    382403# ----------------------------------------- 
     404# Subs: Postprocessing 
     405# ----------------------------------------- 
     406 
     407sub postprocess_data 
     408{ 
     409    # for our first postprocessor, we feed it ALL of the XMLTV files we have 
     410    # as each postprocessor runs, we feed in the output from the previous one 
     411    # Shepherd checks the "completeness" of the data that comes out of a postprocessor & automatically 
     412    # reverts back to the previous postprocessor if it was shown to be bad 
     413 
     414    # first time around: feed in $input_postprocess_files 
     415    my $need_more_data; 
     416 
     417    foreach my $postprocessor (sort { $postprocessors->{$a} <=> $postprocessors->{$b} } keys %$postprocessors) { 
     418        next if ($postprocessors->{$postprocessor}->{disabled}); 
     419 
     420        $postprocessors->{$postprocessor}->{lastdata} = time; 
     421        $postprocessors->{$postprocessor}->{laststatus} = "unknown"; 
     422 
     423        printf "SHEPHERD: Using postprocessor: %s\n",$postprocessor; 
     424 
     425        my $output = "$POSTPROCESSOR_DIR/$postprocessor/output.xmltv"; 
     426        my $comm = "$POSTPROCESSOR_DIR/$postprocessor/$postprocessor " . 
     427                   "--region $region " . 
     428                   "--channels_file $channels_file " . 
     429                   "--output $output"; 
     430        $comm .= " --days $days" if ($days); 
     431        $comm .= " --offset $opt->{offset}" if ($opt->{offset}); 
     432        $comm .= " --debug" if ($debug); 
     433        $comm .= " @ARGV" if (@ARGV); 
     434        $comm .= " $input_postprocess_files"; 
     435        print "SHEPHERD: Excuting command: $comm\n"; 
     436 
     437        chdir "$POSTPROCESSOR_DIR/$postprocessor/"; 
     438        system($comm); 
     439        chdir $CWD; 
     440 
     441        # 
     442        # soak up the data we just collected and check it 
     443        # YES - these are the SAME routines we used in the previous 'grabber' phase 
     444        # but the difference here is that we clear out our 'channel_data' beforehand 
     445        # so we can independently analyze the impact of this postprocessor. 
     446        # if it clearly returns bad data, don't use that data (go back one step) and 
     447        # flag the postprocessor as having failed.  after 3 consecutive failures, disable it 
     448        # 
     449 
     450        # clear out channel_data 
     451        foreach my $ch (keys %{$channels}) { 
     452            delete $channel_data->{$ch}; 
     453        } 
     454 
     455        # process and analyze it! 
     456        &soak_up_data($postprocessor, $output); 
     457        $need_more_data = &analyze_plugin_data($postprocessor_ok_threshold_percent); 
     458 
     459        $postprocessors->{$postprocessor}->{laststatus} = $plugin_data->{$postprocessor}->{laststatus}; 
     460 
     461        if (($need_more_data) && (!$insufficient_grabber_data)) { 
     462            # urgh.  this postprocessor did a bad bad thing ... 
     463            printf "SHEPHERD: XML data from postprocessor %s rejected, using XML from previous stage\n",$postprocessor; 
     464 
     465            if (defined $postprocessors->{$postprocessor}->{conescutive_failures}) { 
     466                $postprocessors->{$postprocessor}->{conescutive_failures}++; 
     467            } else { 
     468                $postprocessors->{$postprocessor}->{conescutive_failures} = 1; 
     469            } 
     470            printf "SHEPHERD: Postprocessor \"%s\" has now failed %d times in a row.  %d more and it will be automatically disabled.\n", 
     471                $postprocessor, 
     472                $postprocessors->{$postprocessor}->{conescutive_failures}, 
     473                $postprocessor_disable_failure_threshold; 
     474 
     475            if ($postprocessors->{$postprocessor}->{conescutive_failures} >= $postprocessor_disable_failure_threshold) { 
     476                printf "SHEPHERD: Disabling Postprocessor \"%s\".\n",$postprocessor; 
     477                $postprocessors->{$postprocessor}->{disabled} = 1; 
     478            } 
     479        } else { 
     480            # accept what this postprocessor did to our output ... 
     481            printf "SHEPHERD: accepting output from postprocessor %s, feeding it into next stage\n",$postprocessor; 
     482            $input_postprocess_files = $output; 
     483            delete $postprocessors->{$postprocessor}->{conescutive_failures} if (defined $postprocessors->{$postprocessor}->{conescutive_failures}); 
     484        } 
     485    } 
     486} 
     487 
     488 
     489sub output_data 
     490{ 
     491    # $input_postprocess_files (hopefully just one file now) contains our final output 
     492    # send it to whereever --output told us to! 
     493 
     494    if ($opt->{output}) { 
     495        open(F,">$opt->{output}") || die "could not open outputfile $opt->{output} for writing: $!\n"; 
     496    } 
     497 
     498    foreach my $infile (split(/ /,$input_postprocess_files)) { 
     499        if (!(open(INFILE,"<$infile"))) { 
     500            printf "WARNING: could not open input file \"%s\": %s\n", $infile, $!; 
     501            printf "Output XMLTV data may be damanged as a result!\n"; 
     502        } else { 
     503            while (<INFILE>) { 
     504                if ($opt->{output}) { 
     505                    print F $_ if ($opt->{output}); 
     506                } else { 
     507                    print $_; 
     508                } 
     509            } 
     510            close(INFILE); 
     511        } 
     512    } 
     513    close(F) if ($opt->{output}); 
     514} 
     515 
     516# ----------------------------------------- 
    383517# Subs: Updates & Installations 
    384518# ----------------------------------------- 
     
    395529 
    396530    my %glist = %$grabbers; 
    397     while ($data =~ /(.*):(.*)/g) 
    398     { 
    399         update_component($1, $2); 
    400         delete $glist{$1}; 
    401     } 
    402     foreach (keys %glist) 
    403     { 
    404         unless ($grabbers->{$_}->{disabled}) 
    405         { 
     531    my %plist = %$postprocessors; 
     532    while ($data =~ /(.*):(.*):(.*)/g) 
     533    { 
     534        my ($proggy, $latestversion, $progtype) = ($1,$2,$3); 
     535        update_component($proggy, $latestversion, $progtype); 
     536        delete $glist{$proggy} if ($progtype eq "grabber"); 
     537        delete $plist{$proggy} if ($progtype eq "postprocessor"); 
     538    } 
     539 
     540    # work out what grabbers disappeared (if any) 
     541    foreach (keys %glist) { 
     542        unless ($grabbers->{$_}->{disabled}) { 
    406543            print "\nDeleted grabber: $_.\n"; 
    407             disable($_); 
     544            disable($_,"grabber"); 
     545            $made_changes = 1; 
     546        } 
     547    } 
     548 
     549    # work out what postprocessors disappeared (if any) 
     550    foreach (keys %plist) { 
     551        unless ($postprocessors->{$_}->{disabled}) { 
     552            print "\nDeleted Postprocessor: $_.\n"; 
     553            disable($_,"postprocessor"); 
     554            $made_changes = 1; 
    408555        } 
    409556    } 
     
    412559sub update_component 
    413560{ 
    414     my ($proggy, $latestversion) = @_; 
     561    my ($proggy, $latestversion, $progtype) = @_; 
    415562 
    416563    print "\n"; 
    417564 
    418     if ($proggy eq $progname) 
    419     { 
    420         if(! -e "$CWD/$progname") 
    421         { 
     565    # handle new installs.. 
     566    if (($$proggy eq $progname) && ($progtype eq "shepherd")) { 
     567        # shepherd itself.. 
     568        if(! -e "$CWD/$progname") { 
    422569            print "Missing: $CWD/$progname\n"; 
    423             install($progname, $latestversion); 
     570            install($progname, $latestversion, $progtype); 
    424571            return; 
    425572        } 
    426     } 
    427     else 
    428     { 
    429         if (!$grabbers->{$proggy} or ! -e "$GRABBER_DIR/$proggy/$proggy") 
    430         { 
     573    } elsif ($progtype eq "grabber") { 
     574        if (!$grabbers->{$proggy} or ! -e "$GRABBER_DIR/$proggy/$proggy") { 
    431575            print "New grabber: $proggy.\n"; 
    432             install($proggy, $latestversion); 
     576            install($proggy, $latestversion, $progtype); 
    433577            return; 
    434578        } 
    435         if ($grabbers->{$proggy}->{disabled}) 
    436         { 
    437             print "Warning: $proggy disabled by config file.\n"; 
    438         } 
    439     } 
    440  
    441     # Compare versions 
    442     my $ver = ($proggy eq $progname ? $version : $grabbers->{$proggy}->{ver}); 
     579        print "Warning: grabber $proggy disabled by config file.\n" if ($grabbers->{$proggy}->{disabled}); 
     580    } elsif ($progtype eq "postprocessor") { 
     581        if (!$postprocessors->{$proggy} or ! -e "$POSTPROCESSOR_DIR/$proggy/$proggy") { 
     582            print "New postprocessor: $proggy.\n"; 
     583            install($proggy, $latestversion, $progtype); 
     584            return; 
     585        } 
     586        print "Warning: postprocessor $proggy disabled by config file.\n" if ($postprocessors->{$proggy}->{disabled}); 
     587    } 
     588 
     589    # upgrade/downgrades 
     590    my $ver; 
     591    if ($progtype eq "grabber") { 
     592        $ver = ($proggy eq $progname ? $version : $grabbers->{$proggy}->{ver}); 
     593    } elsif ($progtype eq "postprocessor") { 
     594        $ver = ($proggy eq $progname ? $version : $postprocessors->{$proggy}->{ver}); 
     595    } elsif (($$proggy eq $progname) && ($progtype eq "shepherd")) { 
     596        $ver = $version; 
     597    } else { 
     598        print "Warning: unknown type of programme: prog '$proggy' progtype '$progtype' not installed.\n"; 
     599        return; 
     600    } 
    443601 
    444602    my $result = versioncmp($ver, $latestversion); 
    445     if ($result == -1) 
    446     { 
     603    if ($result == -1) { 
    447604        print "Upgrading $proggy from v$ver to v$latestversion.\n"; 
    448     } 
    449     elsif ($result == 1) 
    450     { 
     605    } elsif ($result == 1) { 
    451606        print "Downgrading $proggy from v$ver to v$latestversion.\n"; 
    452     } 
    453     else 
    454     { 
     607    } else { 
    455608        print "Already have latest version of $proggy: v$ver.\n"; 
    456609        return; 
    457610    } 
    458     install($proggy, $latestversion); 
     611    install($proggy, $latestversion, $progtype); 
    459612} 
    460613 
    461614sub install 
    462615{ 
    463     my ($proggy, $latestversion) = @_; 
     616    my ($proggy, $latestversion, $progtype) = @_; 
    464617 
    465618    print "Downloading $proggy v$latestversion.\n"; 
     
    469622    my $ver = $version; 
    470623 
    471     if ($proggy ne $progname) 
    472     { 
    473         -d $GRABBER_DIR or mkdir $GRABBER_DIR 
    474             or die "Cannot create directory $GRABBER_DIR: $!"; 
    475  
     624    if (($$proggy eq $progname) && ($progtype eq "shepherd")) { 
     625        $rdir = $HOME; 
     626        $ldir = $CWD; 
     627        $ver = $version; 
     628    } elsif ($progtype eq "grabber") { 
     629        $rdir = $HOME . "/grabbers"; 
    476630        $ldir = "$GRABBER_DIR/$proggy"; 
    477         -d $ldir or mkdir $ldir or die "Cannot create directory $ldir: $!"; 
    478         $rdir = "$rdir/grabbers"; 
    479631        $ver = $grabbers->{$proggy}->{ver}; 
    480     } 
     632        -d $GRABBER_DIR or mkdir $GRABBER_DIR or die "Cannot create directory $GRABBER_DIR: $!"; 
     633    } elsif ($progtype eq "postprocessor") { 
     634        $rdir = $HOME . "/postprocessors"; 
     635        $ldir = "$POSTPROCESSOR_DIR/$proggy"; 
     636        $ver = $postprocessors->{$proggy}->{ver}; 
     637        -d $POSTPROCESSOR_DIR or mkdir $POSTPROCESSOR_DIR or die "Cannot create directory $POSTPROCESSOR_DIR: $!"; 
     638    } else { 
     639        print "Warning: unknown type of programme: prog '$proggy' progtype '$progtype' not installed.\n"; 
     640        return; 
     641    } 
     642    -d $ldir or mkdir $ldir or die "Cannot create directory $ldir: $!"; 
    481643 
    482644    my $newfile = "$ldir/$proggy-$latestversion"; 
    483      
    484645    my $rc = LWP::Simple::getstore("$rdir/$proggy-$latestversion", $newfile); 
    485646 
     
    493654    system('chmod u+x ' . $newfile); 
    494655 
    495     -d $ARCHIVE_DIR or mkdir $ARCHIVE_DIR 
    496       or die "Cannot create directory $ARCHIVE_DIR: $!"; 
     656    -d $ARCHIVE_DIR or mkdir $ARCHIVE_DIR or die "Cannot create directory $ARCHIVE_DIR: $!"; 
    497657 
    498658    if (-e "$ldir/$proggy") 
     
    504664    print "Installed $proggy v$latestversion.\n" if ($debug); 
    505665 
    506     if ($proggy eq $progname) 
    507     { 
     666    # if the update was for shepherd itself, restart it 
     667    if (($$proggy eq $progname) && ($progtype eq "shepherd")) { 
    508668        print "\n*** Restarting ***\n\n"; 
    509669        exec("$ldir/$proggy"); 
     
    512672 
    513673    print "Testing $proggy...\n" if ($debug); 
    514     my $result = test_grabber($proggy); 
    515  
    516     # Update grabbers list 
    517     my $disabled = $grabbers->{$proggy}->{disabled}; 
    518     $grabbers->{$proggy} =  
    519         {  
    520             'ver' => $latestversion,  
    521             'disabled' => $disabled, 
    522             'ready' => $result 
    523         }; 
    524 } 
    525  
    526 sub test_grabber 
    527 { 
    528     my $proggy = shift; 
    529  
    530     chdir("$GRABBER_DIR/$proggy"); 
    531     system("$GRABBER_DIR/$proggy/$proggy --ready"); 
     674    my $result = test_proggy($ldir,"$ldir/$proggy"); 
     675 
     676    if ($progtype eq "grabber") { 
     677        $grabbers->{$proggy}->{ver} = $latestversion; 
     678        $grabbers->{$proggy}->{ready} = $result; 
     679        $grabbers->{$proggy}->{laststatus} = sprintf "updated to %s on %s", $latestversion, (strftime "%a%d%b%y",localtime(time)); 
     680    } elsif ($progtype eq "postprocessor") { 
     681        $postprocessors->{$proggy}->{ver} = $latestversion; 
     682        $postprocessors->{$proggy}->{ready} = $result; 
     683        $postprocessors->{$proggy}->{laststatus} = sprintf "updated to %s on %s", $latestversion, (strftime "%a%d%b%y",localtime(time)); 
     684    } 
     685 
     686    $made_changes = 1; 
     687} 
     688 
     689sub test_proggy 
     690{ 
     691    my ($testdir,$proggyexec) = @_; 
     692 
     693    chdir($testdir); 
     694    system("$proggyexec --ready"); 
    532695    chdir ($CWD); 
    533696 
     
    535698    print "Return value: $result\n" if ($debug); 
    536699 
    537     if ($result) 
    538     { 
    539         print "\nGrabber $proggy did not exit cleanly!\n" . 
    540               "It may require configuration.\n\n"; 
    541     } 
    542  
     700    print "\nprogramme $proggyexec did not exit cleanly!\n" . 
     701         "It may require configuration.\n\n" if ($result); 
    543702    return !$result; 
    544703} 
     
    548707    my $proggy = shift; 
    549708 
     709    # confirm it exists first 
     710    if ((!$grabbers->{$proggy}) && (!$postprocessors->{$proggy})) { 
     711        printf "No such grabber/postprocessor: \"%s\".\n",$proggy; 
     712        return; 
     713    } 
    550714    print "Enabling $proggy.\n"; 
    551     if (!$grabbers->{$proggy}) 
    552     { 
    553         print "No such grabber: \"$proggy\".\n"; 
    554     } 
    555     else 
    556     { 
    557         delete $grabbers->{$proggy}->{disabled}; 
    558     } 
     715 
     716    delete $grabbers->{$proggy}->{disabled} if ($grabbers->{$proggy}); 
     717    delete $postprocessors->{$proggy}->{disabled} if ($postprocessors->{$proggy}); 
     718    $made_changes = 1; 
    559719} 
    560720 
     
    563723    my $proggy = shift; 
    564724 
     725    # confirm it exists first 
     726    if ((!$grabbers->{$proggy}) && (!$postprocessors->{$proggy})) { 
     727        printf "No such grabber/postprocessor: \"%s\".\n",$proggy; 
     728        return; 
     729    } 
    565730    print "Disabling $proggy.\n"; 
    566     $grabbers->{$proggy}->{disabled} = 1; 
     731 
     732    $grabbers->{$proggy}->{disabled} = 1 if ($grabbers->{$proggy}); 
     733    $postprocessors->{$proggy}->{disabled} = 1 if ($postprocessors->{$proggy}); 
     734    $made_changes = 1; 
    567735} 
    568736 
    569737sub set_order 
    570738{ 
    571     my $order = shift; 
    572     my $all_ok = 1; 
    573  
    574     # first check that user supplied a valid list of grabbers 
    575     if ($order) { 
    576         foreach my $proggy (split(/,/,$order)) { 
    577             next if !$proggy; 
    578             if (!$grabbers->{$proggy}) { 
    579                 $all_ok = 0; 
    580                 print "Invalid grabber: \"$proggy\".\n"; 
    581             } 
    582         } 
    583     } 
    584     if ($all_ok) { 
    585         # if list was ok then first reset current order to zero 
    586         foreach my $proggy (keys %$grabbers) { 
    587             $grabbers->{$proggy}->{order} = 0; 
    588         } 
    589  
    590         # and now set order 
    591         my $order_num = 1; 
    592         if ($order) { 
    593             foreach my $proggy (split(/,/,$order)) { 
     739    my ($quiet,$order) = @_; 
     740    $pref_order = $order if ($order); 
     741 
     742    # reset current order to zero 
     743    foreach my $proggy (keys %$grabbers) { 
     744        $grabbers->{$proggy}->{order} = 0; 
     745    } 
     746 
     747    # and now set order 
     748    my $order_num = 1; 
     749    if ($pref_order) { 
     750        foreach my $proggy (split(/,/,$pref_order)) { 
     751            if (defined $grabbers->{$proggy}) { 
    594752                $grabbers->{$proggy}->{order} = $order_num; 
    595753                $order_num++; 
    596754            } 
    597755        } 
    598  
    599         # set order of any grabbers not specified in a random manner 
    600         foreach my $proggy (sort keys %$grabbers) { 
    601             if ($grabbers->{$proggy}->{order} == 0) { 
    602                 $grabbers->{$proggy}->{order} = $order_num+int(rand(100)); 
    603             } 
    604         } 
    605  
    606         # .. and finally normalize the order (& show the user the order we chose) 
    607         print "Grabber order set as follows:\n"; 
    608         $order_num = 0; 
    609         $opt->{setorder} = ""; 
    610         foreach my $proggy (sort { $grabbers->{$a}->{order} <=> $grabbers->{$b}->{order} } keys %$grabbers) { 
    611             $order_num++; 
    612             $grabbers->{$proggy}->{order} = $order_num; 
    613             $opt->{setorder} .= "$proggy" . ","; 
    614             printf " #%d. %s%s\n",$grabbers->{$proggy}->{order},$proggy,($grabbers->{$proggy}->{disabled} ? " [disabled]" : ""); 
    615         } 
    616     } 
     756    } 
     757 
     758    # set order of any grabbers not specified in a random manner 
     759    foreach my $proggy (sort keys %$grabbers) { 
     760        if ((!defined $grabbers->{$proggy}->{order}) || ($grabbers->{$proggy}->{order} == 0)) { 
     761            $grabbers->{$proggy}->{order} = $order_num+int(rand(1000)); 
     762        } 
     763    } 
     764 
     765    # .. and finally normalize the order (& show the user the order we chose) 
     766    print "Grabber order set as follows:\n" unless $quiet; 
     767    $order_num = 0; 
     768    foreach my $proggy (sort { $grabbers->{$a}->{order} <=> $grabbers->{$b}->{order} } keys %$grabbers) { 
     769        $order_num++; 
     770        $grabbers->{$proggy}->{order} = $order_num; 
     771        printf " #%d. %s%s\n",$grabbers->{$proggy}->{order},$proggy,($grabbers->{$proggy}->{disabled} ? " [disabled]" : "") unless $quiet; 
     772    } 
     773 
     774    $made_changes = 1; 
    617775} 
    618776 
     
    620778{ 
    621779    my $result; 
    622     foreach (keys %$grabbers) 
    623     { 
    624         $result = test_grabber($_); 
    625         print "Grabber $_: " .  
    626               ($result ? "OK" : "Failed") . "\n\n"; 
    627         if (!$result ne !$grabbers->{$_}->{ready}) 
    628         { 
    629             $grabbers->{$_}->{ready} = $result; 
     780    foreach my $proggy (keys %$grabbers) { 
     781        $result = test_proggy("$GRABBER_DIR/$proggy","$GRABBER_DIR/$proggy/$proggy"); 
     782        printf "Grabber %s: %s\n",$proggy,($result ? "OK" : "Failed"); 
     783        if (!$result ne !$grabbers->{$proggy}->{ready}) { 
     784            $grabbers->{$proggy}->{ready} = $result; 
     785            $made_changes = 1; 
     786        } 
     787    } 
     788 
     789    foreach my $proggy (keys %$postprocessors) { 
     790        $result = test_proggy("$POSTPROCESSOR_DIR/$proggy","$POSTPROCESSOR_DIR/$proggy/$proggy"); 
     791        printf "Postprocessor %s: %s\n",$proggy,($result ? "OK" : "Failed"); 
     792        if (!$result ne !$postprocessors->{$proggy}->{ready}) { 
     793            $postprocessors->{$proggy}->{ready} = $result; 
     794            $made_changes = 1; 
    630795        } 
    631796    } 
     
    651816        # at least one 'order' was missing .. we need to put it in! 
    652817        printf "Legacy shepherd.conf file didn't contain any grabber order! Automatically updating using a random order, use --setorder to manually set this if you care.\n"; 
    653         &set_order(); 
    654         $opt->{setorder} = ""; 
     818        &set_order(1); 
    655819    } 
    656820} 
     
    691855    open(CONF, ">$config_file") or die "cannot write to $config_file: $!"; 
    692856    print CONF Data::Dumper->Dump( 
    693         [$region,  $grabbers ], 
    694         ["region", "grabbers" ]); 
     857        [$region,  $pref_order,   $grabbers, $postprocessors ], 
     858        ["region", "pref_order", "grabbers", "postprocessors" ]); 
    695859    close CONF; 
    696860    print "\nUpdated configuration file $config_file.\n" if ($debug); 
     
    710874              'help'            => \$opt->{help}, 
    711875              'configure'       => \$opt->{configure}, 
     876              'output'          => \$opt->{output}, 
    712877              'debug'           => \$debug); 
    713878} 
     
    802967 
    803968    print "\nRandomly selecting grabber order.\n\n"; 
    804     set_order(); 
     969    set_order(0); 
    805970 
    806971    show_channels(); 
     
    8821047sub status 
    8831048{ 
    884     print " Grabber                     Version  Enabled  Ready    Last Data\n" . 
    885           " -----------------------------------------------------------------\n"; 
    886     my $star; 
    887     foreach (sort { $grabbers->{$a}->{order} <=> $grabbers->{$b}->{order} } keys %$grabbers) 
    888     { 
     1049    print " Grabber           Version Enabled Ready Last Run   Status\n" . 
     1050          " ----------------- ------- ------- ----- ---------- ---------------------------\n"; 
     1051    foreach (sort { $grabbers->{$a}->{order} <=> $grabbers->{$b}->{order} } keys %$grabbers) { 
    8891052        my $h = $grabbers->{$_}; 
    890         printf  " %-26s %8s %5s %7s %10s\n", 
     1053        printf  " %-16s %8s %4s %6s  %11s %s\n", 
    8911054                "$h->{order}. $_", 
    8921055                $h->{ver}, 
    8931056                $h->{disabled} ? '' : 'Y', 
    8941057                $h->{ready} ? 'Y' : '', 
    895                 $h->{lastdata} ? localtime($h->{lastdata}) : ''; 
    896     } 
    897  
    898     printf "Grabbers shown in order of preference.\n"; 
     1058                $h->{lastdata} ? (strftime "%a%d%b%y", localtime($h->{lastdata})) : '', 
     1059                $h->{laststatus} ? $h->{laststatus} : ''; 
     1060    } 
     1061    printf "Grabbers shown in order of preference.\n\n"; 
     1062 
     1063    print " Postprocessor     Version Enabled Ready Last Run   Status\n" . 
     1064          " ----------------- ------- ------- ----- ---------- ---------------------------\n"; 
     1065    foreach (sort { $postprocessors->{$a} <=> $postprocessors->{$b} } keys %$postprocessors) { 
     1066        my $h = $postprocessors->{$_}; 
     1067        printf  " %-16s %8s %4s %6s  %11s %s\n", 
     1068                $_, 
     1069                $h->{ver}, 
     1070                $h->{disabled} ? '' : 'Y', 
     1071                $h->{ready} ? 'Y' : '', 
     1072                $h->{lastdata} ? (strftime "%a%d%b%y", localtime($h->{lastdata})) : '', 
     1073                $h->{laststatus} ? $h->{laststatus} : ''; 
     1074    } 
     1075    printf "Postprocessors shown in order of execution.\n\n"; 
    8991076} 
    9001077 
     
    9121089 
    9131090    --setorder <s>        Set order of grabbers to <s> (comma-seperated list of grabbers) 
    914     --disable <s>         Don't ever use grabber <s> 
     1091 
     1092    --disable <s>         Don't ever use grabber/postprocessor <s> 
    9151093    --enable <s>          Okay, maybe use it again then 
    916     --uninstall <s>       Remove a disabled grabber 
     1094    --uninstall <s>       Remove a disabled grabber/postprocessor 
    9171095 
    9181096    --noupdate            Do not attempt to update before running 
    9191097    --update              Update only; do not grab data 
    9201098 
    921     --check               Check status of all grabbers 
     1099    --check               Check status of all grabbers and postprocessors 
    9221100}; 
    9231101    exit 0;