Subversion Repositories DevTools

Rev

Rev 7389 | Rev 7397 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1038 dpurdie 1
#! /usr/bin/perl
2
########################################################################
3
# Copyright (C) 2011 Vix-ERG Limited, All rights reserved
4
#
5
# Module name   : blatDaemon.pl
6
# Module type   :
7
# Compiler(s)   : Perl
8
# Environment(s):
9
#
10
# Description   :
11
#
12
# Usage         :   ARGV[0] - Path to config file for this instance
13
#
14
#......................................................................#
15
 
16
require 5.008_002;
17
use strict;
18
use warnings;
19
use Getopt::Long;
20
use File::Basename;
1040 dpurdie 21
use Data::Dumper;
22
use File::Spec::Functions;
1038 dpurdie 23
use POSIX ":sys_wait_h";
24
use File::Temp qw/tempfile/;
6320 dpurdie 25
use Digest::MD5;
1038 dpurdie 26
 
27
use FindBin;                                    # Determine the current directory
28
use lib "$FindBin::Bin/lib";                    # Allow local libraries
29
 
30
use Utils;
31
use StdLogger;                                  # Log to sdtout
32
use Logger;                                     # Log to file
33
 
34
#
35
#   Database interface
36
#   Pinched from jats and modified so that this software is not dependent on JATS
37
#
38
use IO::Handle;
39
use JatsRmApi;
40
use DBI;
41
 
42
#
43
#   Globals
44
#
45
my $logger = StdLogger->new();                  # Stdout logger. Only during config
6475 dpurdie 46
$logger->err("No config file specified") unless (defined $ARGV[0]);
7387 dpurdie 47
$logger->err("Config File does not exist: $ARGV[0]") unless (-f $ARGV[0]);
1038 dpurdie 48
my $name = basename( $ARGV[0]);
49
   $name =~ s~.conf$~~;
50
my $now = 0;
5398 dpurdie 51
my $startTime = 0;
3515 dpurdie 52
my $tar = 'tar';
53
my $gzip = 'gzip';
1038 dpurdie 54
my $tagDirTime = 0;
55
my $lastDirScan = 0;
56
my $lastReleaseScan = 0;
1040 dpurdie 57
my $releaseScanMode = 0;
6779 dpurdie 58
my $lastTagListUpdate = 0;
6776 dpurdie 59
my $lastRmConfRead = 0;
60
my $lastRmConfFullRead = 0;
61
my $lastRmSeqNum = 0;
1038 dpurdie 62
my $mtimeConfig = 0;
63
my $conf;
64
my $extraPkgs;
1040 dpurdie 65
my $excludePkgs;
66
my %releaseData;
3515 dpurdie 67
my $comError = 0;
5398 dpurdie 68
my $yday = -1;
6148 dpurdie 69
my $RemotePkgList = {};
6320 dpurdie 70
my $targetBinDir = "$FindBin::Bin/targetBin";
6776 dpurdie 71
my $server_id;
72
my @projectList;
73
my @releaseList;
7387 dpurdie 74
my $isS3Target;
1038 dpurdie 75
 
76
#
5398 dpurdie 77
#   Contain statisics maintained while operating
78
#       Can be dumped with a kill -USR2
79
#       List here for documentation
80
#  
81
 
82
my %statistics = (
83
    SeqNum => 0,                        # Bumped when $statistics are dumped
84
    timeStamp => 0,                     # DateTime when statistics are dumped
85
    upTime => 0,                        # Seconds since program start
86
    Cycle => 0,                         # Major process loop counter
87
    phase => 'Init',                    # Current phase of operation
88
    state => 'OK',                      # Nagios state
89
                                        # 
90
                                        # The following are reset each day
91
    dayStart => 0,                      # DateTime when daily data was reset
92
    txCount => 0,                       # Packages Transferred
93
    delCount => 0,                      # Packages marked for deletion
94
    staleTags => 0,                     # Stale Tags
95
    linkErrors => 0,                    # Transfer errors
96
                                        # 
97
                                        # Per Cycle Data - Calculated each processing Cycle
5399 dpurdie 98
    total    => 0,                      # Packages to be synced
5398 dpurdie 99
    delete   => 0,                      # Packages to delete
100
    excluded => 0,                      # Packages excluded    
101
    filtered => 0,                      # Packages filtered out
102
    missing  => 0,                      # Packages missing
103
    transfer => 0,                      # Packages to transfer
104
    writable => 0,                      # Packages still writable - thus not transferred
105
    tagCount => 0,                      # Packages tagged to be transferred
7394 dpurdie 106
    tagDelCount => 0,                   # Packages tagged to be deleted
5404 dpurdie 107
                                        #
108
                                        # Expected from the Target 
109
#   Target.Hostname => '',              # Target Hostname
110
#   Target.avail    => 0,               # Information from 'df' 1Kblocks 
111
#   Target.pcent    => 0,
112
#   Target.size     => 0,
113
#   Target.used     => 0,
114
#   Target.iavail   => 0,               # Inode information from 'df'
115
#   Target.ipcent   => 0,
116
#   Target.isize    => 0,
117
#   Target.iused    => 0,
118
#   Target.Total    => 0,               # Number of Package Versions in the archive
119
#   Target.Damaged  => 0,               # Number that are damaged
120
#   Target.Delete   => 0,               # Number marked for future deletion
121
#   Target.Missing  => 0,               # Number missing
5398 dpurdie 122
);
123
 
1038 dpurdie 124
#
5398 dpurdie 125
#   Describe configuration parameters
126
#
1038 dpurdie 127
my %cdata = (
128
    '.ignore'         => {'pkg\.(.+)' => 'pkgs' },
7387 dpurdie 129
    '.oneOf'          => [['hostname','S3Bucket']] ,
130
 
1048 dpurdie 131
    'piddir'          => {'mandatory' => 1      , 'fmt' => 'dir'},
132
    'sleep'           => {'default'   => 5      , 'fmt' => 'period'},
133
    'dpkg_archive'    => {'mandatory' => 1      , 'fmt' => 'dir'},
134
    'logfile'         => {'mandatory' => 1      , 'fmt' => 'vfile'},
135
    'logfile.size'    => {'default'   => '1M'   , 'fmt' => 'size'},
136
    'logfile.count'   => {'default'   => 9      , 'fmt' => 'int'},
137
    'verbose'         => {'default'   => 0      , 'fmt' => 'int'},
6776 dpurdie 138
    'rmHostName'      => {'default'   => undef  , 'fmt' => 'text'},
3847 dpurdie 139
    'sshport'         => {'default'   => 0      , 'fmt' => 'int'},
1048 dpurdie 140
    'tagdir'          => {'mandatory' => 1      , 'fmt' => 'dir'},
141
    'forcedirscan'    => {'default'   => 100    , 'fmt' => 'period'},
142
    'tagage'          => {'default'   => '10m'  , 'fmt' => 'period'},
143
    'tagListUpdate'   => {'default'   => '1h'   , 'fmt' => 'period'},
7387 dpurdie 144
    'tagMaxPackages'  => {'default'   => 10     , 'fmt' => 'int'},
6776 dpurdie 145
    'rmConfigCheck'   => {'default'   => '60'   , 'fmt' => 'period'},
146
    'rmConfFullRead'  => {'default'   => '1h'   , 'fmt' => 'period'},
1048 dpurdie 147
    'synctime'        => {'default'   => '2h'   , 'fmt' => 'period'},
148
    'syncretry'       => {'default'   => '5m'   , 'fmt' => 'period'},
149
    'allProjects'     => {'default'   => 0      , 'fmt' => 'bool'},
4456 dpurdie 150
    'allArchive'      => {'default'   => 0      , 'fmt' => 'bool'},
1048 dpurdie 151
    'project'         => {'mandatory' => 0      , 'fmt' => 'int_list'},
152
    'release'         => {'mandatory' => 0      , 'fmt' => 'int_list'},
153
    'writewindow'     => {'default'   => '3h'   , 'fmt' => 'period'},
154
    'maxpackages'     => {'default'   => 5      , 'fmt' => 'int'},
155
    'deletePackages'  => {'default'   => 0      , 'fmt' => 'bool'},
156
    'deleteImmediate' => {'default'   => 0      , 'fmt' => 'bool'},
157
    'deleteAge'       => {'default'   => 0      , 'fmt' => 'period'},
158
    'packageFilter'   => {'default'   => undef  , 'fmt' => 'text'},
159
    'active'          => {'default'   => 1      , 'fmt' => 'bool'},
6475 dpurdie 160
    'debug'           => {'default'   => 0      , 'fmt' => 'bool'},                 # Log to screen
161
    'txdetail'        => {'default'   => 0      , 'fmt' => 'bool'},
6776 dpurdie 162
    'noTransfers'     => {'default'   => 0      , 'fmt' => 'bool'},                 # Debugging option to prevent transfers
7394 dpurdie 163
    'maxTarZips'      => {'default'   => 5      , 'fmt' => 'int'},
7387 dpurdie 164
 
165
    #
166
    #   Transfer via ssh
167
    #   Cannot be used in conjunction with S3Bucket
168
    #    
169
    'hostname'        => {'fmt' => 'text', requires => 'user,identity,bindir'},
170
    'user'            => {'fmt' => 'text', requires => 'hostname'},
171
    'identity'        => {'fmt' => 'file'},
172
    'bindir'          => {'fmt' => 'text'},
173
 
174
    #
175
    #   Transfer to S3 configuration items
176
    #       Cannot be used in conjunction with hostname
177
    #       Many other options will be ignored 
178
    #
179
    'S3Bucket'        => {'fmt' => 'text', requires => 'S3Profile'},
180
    'S3Profile'       => {'fmt' => 'text', requires => 'S3Bucket' },
1038 dpurdie 181
);
182
 
183
 
184
#
185
#   Read in the configuration
186
#       Set up a logger
187
#       Write a pidfile - thats not used
6776 dpurdie 188
$now = $startTime = time();
1038 dpurdie 189
readConfig();
190
Utils::writepid($conf);
191
$logger->logmsg("Starting...");
5404 dpurdie 192
readStatistics();
1038 dpurdie 193
sighandlers($conf);
194
 
195
#
196
#   Main processing loop
197
#   Will exit when terminated by parent
198
#
6779 dpurdie 199
while (1)
1038 dpurdie 200
{
201
    $logger->verbose3("Processing");
5398 dpurdie 202
    $statistics{Cycle}++;
1038 dpurdie 203
    $now = time();
1040 dpurdie 204
 
5398 dpurdie 205
    $statistics{phase} = 'ReadConfig';
1038 dpurdie 206
    readConfig();
1048 dpurdie 207
    if ( $conf->{'active'} )
208
    {
5398 dpurdie 209
        $statistics{phase} = 'ProcessReleaseList';
1048 dpurdie 210
        processReleaseList();
5398 dpurdie 211
        $statistics{phase} = 'processTags';
1048 dpurdie 212
        processTags();
5398 dpurdie 213
        $statistics{phase} = 'maintainTagList';
1048 dpurdie 214
        maintainTagList();
215
    }
1040 dpurdie 216
    %releaseData = ();
217
 
5398 dpurdie 218
    $statistics{phase} = 'Sleep';
1038 dpurdie 219
    sleep( $conf->{'sleep'} );
6779 dpurdie 220
    reapChildren();
1050 dpurdie 221
 
3847 dpurdie 222
    #   If my PID file ceases to be, then exit the daemon
223
    #   Used to force daemon to restart
224
    #
225
    unless ( -f $conf->{'pidfile'} )
226
    {
227
        $logger->logmsg("Terminate. Pid file removed");
228
        last;
229
    }
1038 dpurdie 230
}
5398 dpurdie 231
$statistics{phase} = 'Terminated';
1038 dpurdie 232
$logger->logmsg("Child End");
233
exit 0;
234
 
235
#-------------------------------------------------------------------------------
6779 dpurdie 236
# Function        : reapChildren 
237
#
238
# Description     : Reap any and all dead children
239
#                   Call in major loops to prevent zombies accumulating 
240
#
241
# Inputs          : None
242
#
243
# Returns         : 
244
#
245
sub reapChildren
246
{
247
    my $currentPhase = $statistics{phase};
248
    $statistics{phase} = 'Reaping';
249
 
250
    my $kid;
251
    do {
252
        $kid = waitpid(-1, WNOHANG);
253
    } while ( $kid > 0 );
254
 
255
    $statistics{phase} = $currentPhase;
256
}
257
 
258
 
259
#-------------------------------------------------------------------------------
1038 dpurdie 260
# Function        : readConfig
261
#
262
# Description     : Re read the config file if it modification time has changed
263
#
264
# Inputs          : Nothing
265
#
1289 dpurdie 266
# Returns         : 0       - Config not read
267
#                   1       - Config read
268
#                             Config file has changed
1038 dpurdie 269
#
270
sub readConfig
271
{
272
    my ($mtime) = Utils::mtime($ARGV[0]);
1289 dpurdie 273
    my $rv = 0;
274
 
1038 dpurdie 275
    if ( $mtimeConfig != $mtime )
276
    {
277
        $logger->logmsg("Reading config file: $ARGV[0]");
278
        $mtimeConfig = $mtime;
279
        my $errors;
280
        ($conf, $errors) = Utils::readconf ( $ARGV[0], \%cdata );
281
        if ( scalar @{$errors} > 0 )
282
        {
283
            warn "$_\n" foreach (@{$errors});
284
            die ("Config contained errors\n");
285
        }
7387 dpurdie 286
        $isS3Target = defined $conf->{'S3Bucket'};
1038 dpurdie 287
 
288
        #
289
        #   Reset some information
290
        #   Create a new logger
291
        #
7387 dpurdie 292
        $logger = Logger->new($conf) unless $conf->{debug};
1038 dpurdie 293
        $conf->{logger} = $logger;
294
        $conf->{'pidfile'} = $conf->{'piddir'} . '/' . $name . '.pid';
7387 dpurdie 295
        $logger->setVerbose($conf->{verbose});
1038 dpurdie 296
        $logger->verbose("Log Levl: $conf->{verbose}");
297
 
298
        #
5398 dpurdie 299
        #   Setup statistics filename
300
        $conf->{'statsfile'} = $conf->{'piddir'} . '/' . $name . '.stats';
301
        $conf->{'statsfiletmp'} = $conf->{'piddir'} . '/' . $name . '.stats.tmp';
302
 
303
        #
1038 dpurdie 304
        #   Extract extra package config
6475 dpurdie 305
        #       Ignore ALL and Version info if transferring the entire archive
306
        #       Honor the EXCLUDE - for bandwidth reasons
7387 dpurdie 307
        #       
308
        #   NOTE: Package inclusion will not be processed in allArchive mode
309
        #   BUG:  This processing should be done after the RmConfig has been included
1038 dpurdie 310
        #
311
        $extraPkgs = {};
1040 dpurdie 312
        $excludePkgs = {};
1038 dpurdie 313
        while (my($key, $data) = each ( %{$conf->{pkgs}} ))
314
        {
3846 dpurdie 315
            if ( $data eq 'EXCLUDE' ) {
1040 dpurdie 316
                $excludePkgs->{$key} = 1;
317
                $logger->verbose("Exclude Pkg: $key");
3846 dpurdie 318
 
319
            } elsif ( $data eq 'ALL' ) {
6475 dpurdie 320
                next if ( $conf->{'allArchive'} );
3846 dpurdie 321
                foreach my $pver (getPackageVersions($key))
322
                {
323
                    $extraPkgs->{$key}{$pver} = 1;
324
                    $logger->verbose("Extra Pkg: $key -> $pver");
325
                }
326
            } else {
6475 dpurdie 327
                next if ( $conf->{'allArchive'} );
328
                foreach (split(/[,\s]+/, $data))
329
                {
330
                    $extraPkgs->{$key}{$_} = 1;
331
                    $logger->verbose("Extra Pkg: $key -> $_");
332
                }
1040 dpurdie 333
            }
1038 dpurdie 334
        }
1046 dpurdie 335
 
1048 dpurdie 336
        $logger->verbose("Filter Packages: " . $conf->{'packageFilter'})
337
            if ( defined $conf->{'packageFilter'} );
338
 
3847 dpurdie 339
        $logger->warn("Non standard ssh port: " . $conf->{'sshport'})
340
            if ( $conf->{'sshport'} );
1048 dpurdie 341
 
6776 dpurdie 342
        #
343
        #   Save Text based config for use in RmConfig
344
        #
345
        $conf->{'BaseActive'} = $conf->{'active'};
1048 dpurdie 346
 
6776 dpurdie 347
        #
348
        #   Flag config has changed / been read
349
        #   Force full RM data fetch
350
        #
351
        $rv = 1;
352
        $lastRmSeqNum = 0;
353
        $lastRmConfRead = 0;
354
    }
4456 dpurdie 355
 
6776 dpurdie 356
    #
357
    #   Read the Release Manager configuration too
358
    #
359
    $rv |= ReadRmConfig();
360
 
361
    #
362
    #   When config is read force some actions
363
    #       - Force tagList to be created
364
    #       - Force release scan
365
    if ($rv) {
6779 dpurdie 366
        $lastTagListUpdate = 0;
1050 dpurdie 367
        $lastReleaseScan = 0;
6776 dpurdie 368
 
369
        #
370
        #   Update global Project/Release list - only on change
371
        @projectList = split /[,\s]+/, $conf->{'project'} || '';
372
        @releaseList = split /[,\s]+/, $conf->{'release'} || '';
373
 
374
        $logger->logmsg("projectList: ". join(',',@projectList));
375
        $logger->logmsg("releaseList: ". join(',',@releaseList));
376
 
377
#Utils::DebugDumpData ("Config", $conf);
378
 
379
        $logger->warn("Transfer session configured as not active") unless ( $conf->{'active'} );
380
        $logger->warn("Transfer all project packages") if ( $conf->{'allProjects'} );
381
        $logger->warn("Transfer entire package archive") if ( $conf->{'allArchive'} );
382
        $logger->warn("All Transfers disabled") if ( $conf->{'noTransfers'} );
7387 dpurdie 383
        $logger->warn("Transfer to AWS S3 Bucket") if ( $isS3Target );
6776 dpurdie 384
    }
385
 
386
    return $rv;
387
}
388
 
389
#-------------------------------------------------------------------------------
390
# Function        : ReadRmConfig 
391
#
392
# Description     : Read Configuration information from Release Manager
393
#                   If Rm configuration is to be used then it will override
394
#                   the project/release configuration in the text file
395
#                    
396
#
397
# Inputs          : Nothing
398
#
399
# Returns         : 0       - Config not read
400
#                   1       - Config read
401
#                             Config file has changed
402
#
403
sub ReadRmConfig
404
{
405
    #
406
    #   Time to perform a database read
407
    #   Will do at startup and every time period there after
408
    #
409
    return 0 unless $conf->{'rmHostName'};
410
    return 0 unless $conf->{'BaseActive'};
411
    return 0 unless ( $now > ($lastRmConfRead + $conf->{rmConfigCheck} ));
412
    $logger->verbose("ReadRmConfig");
413
    $lastRmConfRead = $now;
414
 
415
    my $rv = 0;
416
    my $RM_DB;
417
    my ($blat_seqnum, $blat_mode, $found);
418
    my $server_enabled = 1;
419
 
420
    #
421
    #   Read the BLAT_SERVER record from Release Manager
422
    #
423
    my $m_sqlstr = "select blat_id, blat_enable, blat_seqnum, blat_mode from RELEASE_MANAGER.blat_servers WHERE UPPER(BLAT_SERVER_NAME) = UPPER('$conf->{'rmHostName'}')";
424
 
425
    connectRM(\$RM_DB);
426
    my $sth = $RM_DB->prepare($m_sqlstr);
427
    if ( defined($sth) )
428
    {
429
        if ( $sth->execute( ) )
430
        {
431
            if ( $sth->rows )
432
            {
433
                while (my @row = $sth->fetchrow_array )
434
                {
435
                    $logger->verbose2("ReadRmConfig:Data:@row");
436
 
437
                    $server_id = $row[0] || 0;
438
                    $server_enabled = ($row[1] || 'N') eq 'Y'; 
439
                    $blat_seqnum = $row[2] || 0;
440
                    $blat_mode = ($row[3] || 'N');
441
                    $found = 1;
442
                    last;
443
                }
444
            }
445
            $sth->finish();
446
        }
447
        else
448
        {
449
            $logger->warn("ReadRmConfig: SQL Execute failure");
450
        }
451
    }
452
    else
453
    {
454
        $logger->warn("ReadRmConfig: SQL Prepare failure");
455
    }
456
    disconnectRM(\$RM_DB);
457
 
458
    #
459
    #   Process the extracted data
460
    #   
461
    if ( !$found) {
462
        $logger->warn("No Release Manager configuration for:" . $conf->{'rmHostName'});
463
        $server_id = 0;
464
        $lastRmSeqNum = 0;
465
        return 0;
466
    }
467
 
468
    #       Check for a change in config
469
    #
470
    if ($lastRmSeqNum != $blat_seqnum) {
1289 dpurdie 471
        $rv = 1;
6776 dpurdie 472
        $lastRmSeqNum = $blat_seqnum;
1038 dpurdie 473
    }
6776 dpurdie 474
 
475
    #
476
    #   Insert server configuration into the global config table
477
    #   Rm Config will override the text config
478
    #
479
    $conf->{'active'} = $server_enabled;
480
    $conf->{'allProjects'} = $blat_mode eq 'P';
481
    $conf->{'allArchive'} =  $blat_mode eq 'E';
482
 
483
    #
484
    #   If the config has changed, then read the Project and Release records from the database
485
    #   Insert these into the config provided by the text file confing
486
    #
487
    my $forceFullRead;
488
    if ( $now > ($lastRmConfFullRead + $conf->{rmConfFullRead} )) {
489
        $logger->verbose("ForceReadRmConfig");
490
        $lastRmConfFullRead = $now;
491
        $forceFullRead = 1;
492
    }
493
 
494
    if ($rv || $forceFullRead) {
495
        connectRM(\$RM_DB);
496
        my @projects;
497
        my @releases;
498
 
499
        my $m_sqlstr = "select proj_id as proj_id, null as rtag_id from release_manager.blat_projects where blat_id = $server_id and UPPER(bp_enabled) = 'Y'" .
500
                       " UNION " .
501
                       "select null as proj_id, rtag_id rtag_id from release_manager.blat_releases where blat_id = $server_id and UPPER(br_enabled) = 'Y'";
502
 
503
        my $sth = $RM_DB->prepare($m_sqlstr);
504
        if ( defined($sth) )
505
        {
506
            if ( $sth->execute( ) )
507
            {
508
                if ( $sth->rows )
509
                {
510
                    while (my @row = $sth->fetchrow_array )
511
                    {
512
                        $logger->verbose2("ReadRmConfig:Data:@row");
513
                        push (@projects, $row[0]) if ($row[0]);
514
                        push (@releases, $row[1]) if ($row[1] );
515
                    }
516
                }
517
                $sth->finish();
518
            }
519
            else
520
            {
521
                $logger->warn("ReadRmConfig: SQL Execute failure");
522
            }
523
        }
524
        else
525
        {
526
            $logger->warn("ReadRmConfig: SQL Prepare failure");
527
        }
528
        disconnectRM(\$RM_DB);
529
 
530
        #
531
        #   Reform the projects and releases into a comma separated list and detect changes
532
        #
533
        my $projects = join(',', sort @projects); 
534
        my $releases = join(',', sort @releases);
535
 
536
        if ((($projects || '') ne ($conf->{'project'} || '')) || (($releases || '') ne  ($conf->{'release'} || ''))) {
537
            $conf->{'project'} = $projects;
538
            $conf->{'release'} = $releases;
539
            $rv = 1;
540
        }
541
    }
542
 
543
    # Indicate if the config has changed
1289 dpurdie 544
    return $rv;
1038 dpurdie 545
}
546
 
6475 dpurdie 547
#-------------------------------------------------------------------------------
548
# Function        : checkForBasicTools 
549
#
550
# Description     : Check that the target has the basic tools are installed
551
#                   Can populate the target's bin directory with tools
552
#
553
# Inputs          : None 
554
#
555
# Returns         : Nothing
556
#
557
sub checkForBasicTools
558
{
7387 dpurdie 559
    return if $isS3Target;
560
 
6475 dpurdie 561
    my $ph;
562
    my $found;
563
    my $tgt_cmd = "if [ -x  $conf->{'bindir'}/get_plist.pl ] ; then echo :FOUND:; fi";
564
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 565
 
6475 dpurdie 566
    $logger->verbose2("checkForBasicTools:ssh_cmd:$ssh_cmd");
567
    open ($ph, "$ssh_cmd |");
568
    while ( <$ph> )
569
    {
570
        chomp;
571
        if (m~:FOUND:~) {
572
            $found = 1;
573
        }
574
        $logger->verbose2("checkForBasicTools:Data: $_");
575
    }
576
    close ($ph);
577
    my $exitCode = $? >> 8;
578
    $logger->verbose2("checkForBasicTools:End: $exitCode, $?");
579
 
580
    unless ( $found )
581
    {
582
        $logger->warn("checkForBasicTools: None found, $?");
583
 
584
        #
585
        #   The 'get_plist.pl' program was not found
586
        #   Assume that the entire directory does not exist and transfer all
587
        #
588
        transferTargetBin();
589
    }
590
}
591
 
1038 dpurdie 592
#-------------------------------------------------------------------------------
6475 dpurdie 593
# Function        : transferTargetBin 
594
#
595
# Description     : Ensure that the targets 'bin' folder is upto date 
596
#
597
# Inputs          : $blatBinData    - Ref to array of target data file info
598
#
599
# Returns         : 
600
#
601
sub transferTargetBin
602
{
7389 dpurdie 603
    return if $isS3Target;
6475 dpurdie 604
    my ($blatBinData) = @_;
605
 
606
    my $blatBinList = getBlatBin();
607
    foreach my $file ( keys %{$blatBinList} )
608
    {
609
        if (defined $blatBinData && exists $blatBinData->{$file}) {
610
            if ($blatBinData->{$file} eq $blatBinList->{$file}) {
611
                delete $blatBinList->{$file};
612
            }
613
        }
614
    }
615
#Utils::DebugDumpData ("blatBinList", $blatBinList);
616
    transferBlatBin($blatBinList);
617
}
618
 
619
#-------------------------------------------------------------------------------
1038 dpurdie 620
# Function        : processReleaseList
621
#
622
# Description     : Process the release list
623
#                       Determine if its time to process release list
624
#                       Determine release list
625
#                       Determine release content
626
#                       Determine new items
627
#
628
# Inputs          : None
629
#
630
# Returns         : Nothing
631
#
632
sub processReleaseList
633
{
634
    #
1044 dpurdie 635
    #   Is Release List Processing active
636
    #   Can configure blat to disable release sync
637
    #   This will then allow 'new' packages to be sent
638
    #
7389 dpurdie 639
    if ( $conf->{maxpackages} == 0 || $conf->{'synctime'} <= 0 )
1044 dpurdie 640
    {
641
        $logger->verbose2("processReleaseList disabled");
7387 dpurdie 642
        $RemotePkgList = {};
1044 dpurdie 643
        return;
644
    }
645
 
646
    #
1038 dpurdie 647
    #   Time to perform the scan
648
    #   Will do at startup and every time period there after
649
    #
1040 dpurdie 650
    my $wtime = $releaseScanMode ? $conf->{'syncretry'} : $conf->{'synctime'};
651
    return unless ( $now > ($lastReleaseScan + $wtime ));
1038 dpurdie 652
    $logger->verbose("processReleaseList");
653
    $lastReleaseScan = $now;
1040 dpurdie 654
    $releaseScanMode = 1;                                   # Assume error
1038 dpurdie 655
 
656
    #
6772 dpurdie 657
    #   Ensure that we have the basic tools for the transfer
658
    #
659
    checkForBasicTools();
660
 
661
    #
1038 dpurdie 662
    #   Get list of packages from Remote site
663
    #
664
    my $remotePkgList;
5404 dpurdie 665
    my $remoteData;
6320 dpurdie 666
    my $blatBinData;
1038 dpurdie 667
 
7389 dpurdie 668
    #
669
    #   Get Data from an S3 bucket
670
    #   Can only get a part of the full data set. The timestamp can't be processed, so set it to -1
671
    #   
672
    if ($isS3Target ) {
673
        my $ph;
674
        my $tgt_cmd = "aws s3 ls $conf->{'S3Bucket'}/";
675
 
676
        $logger->verbose2("processReleaseList:s3_cmd:$tgt_cmd");
677
        open ($ph, "$tgt_cmd |");
678
        while ( <$ph> )
1038 dpurdie 679
        {
7389 dpurdie 680
            chomp;
681
            m~.*\s(.*)__(.*).tgz$~;
682
            $remotePkgList->{$1}{$2}{s3} = 1;
1038 dpurdie 683
        }
7389 dpurdie 684
        close ($ph);
685
 
686
    } else {
687
        # Get Data from a dpkg_archive maintained via ssh
688
        #   Invoke a program on the remote site and parse the results
689
        #
690
        #   Returned data looks like:
691
        #       Metadata avail="140100452"
692
        #       BlatBin MD5="9e2c6e45af600a20a01dbcb7570da1f1" file="stat.pl"
693
        #       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas"
694
        #       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas" "link=latest"
695
        #       time="1497954104" GMT="Tue Jun 20 10:21:44 2017" pname="ERGissaccounts" pver="1.0.7178.mas" deleted="0"
696
 
697
        my $ph;
698
        my $tgt_cmd = "$conf->{'bindir'}/get_plist.pl";
699
        my $ssh_cmd = sshCmd($tgt_cmd);
700
 
701
        $logger->verbose2("processReleaseList:ssh_cmd:$ssh_cmd");
702
        open ($ph, "$ssh_cmd |");
703
        while ( <$ph> )
6320 dpurdie 704
        {
7389 dpurdie 705
            chomp;
706
            if ($_ =~ m~^Metadata\s+~)
5404 dpurdie 707
            {
7389 dpurdie 708
                parsePkgMetaData($_, \%{$remoteData});
5404 dpurdie 709
            }
7389 dpurdie 710
            elsif ($_ =~ m~^BlatBin\s+~)
711
            {
712
                parseBlatBinData($_, \%{$blatBinData})
713
            }
5404 dpurdie 714
            else
715
            {
7389 dpurdie 716
                if ( parsePkgList($_, \%{$remotePkgList} ) )
717
                {
718
                    $logger->verbose2("processReleaseList:Data: $_");
719
                }
720
                else
721
                {
722
                    $logger->warn("processReleaseList:Bad Data: $_");
723
                }
5404 dpurdie 724
            }
1038 dpurdie 725
        }
7389 dpurdie 726
        close ($ph);
1038 dpurdie 727
    }
728
    $logger->verbose("processReleaseList:End: $?");
6148 dpurdie 729
    $RemotePkgList = $remotePkgList; 
730
 
3515 dpurdie 731
    LogTxError ($?);
1038 dpurdie 732
    if ( $? != 0 )
733
    {
734
        $logger->warn("Cannot retrieve package list: $?");
5398 dpurdie 735
        $statistics{state} = 'No Remote Package List';
1038 dpurdie 736
        return;
737
    }
1042 dpurdie 738
#Utils::DebugDumpData ("remotePkgList", $remotePkgList);
6320 dpurdie 739
 
1038 dpurdie 740
    #
6475 dpurdie 741
    #   Ensure that the target bin folder is up to date
742
    #
743
    transferTargetBin($blatBinData);
6320 dpurdie 744
 
745
    #
1038 dpurdie 746
    #   Determine the set of packages in the releases to be transferred
4456 dpurdie 747
    # 
748
    my $pkgList;
749
    if ( $conf->{'allArchive'} )
1040 dpurdie 750
    {
4456 dpurdie 751
        #   Examine entire archive
752
        #
753
        $pkgList = getArchiveList();
1040 dpurdie 754
    }
4456 dpurdie 755
    else
756
    {
757
        #   Examine Releases
758
        #
759
        my @rlist = getReleaseList();
760
        unless ( @rlist )
761
        {
762
            $logger->verbose2("No Releases to Process");
5398 dpurdie 763
            $statistics{state} = 'No Releases found';
6475 dpurdie 764
 
765
            #   Allow config with just specified packages
766
            #
767
            #   return;
768
        } else {
769
            $pkgList = getPkgList(@rlist);
4456 dpurdie 770
        }
771
    }
1038 dpurdie 772
 
773
    #
774
    #   Append extra packages
775
    #   These are packages that are specifically named by the user
776
    #
6475 dpurdie 777
    #   Note: If they are symbolic links, then the target of the
778
    #         link is also added.
1038 dpurdie 779
    #
780
    #         Symlink MUST be within the same directory
3846 dpurdie 781
    #           Used to transfer jats2_current
1038 dpurdie 782
    #
783
    while ( (my ($pname, $pvers)) = each %{$extraPkgs} ) {
784
        while ( (my ($pver, $pdata) ) = each %{$pvers} ) {
785
 
1040 dpurdie 786
            my $epath = catfile( $conf->{'dpkg_archive'} , $pname, $pver );
1038 dpurdie 787
            if ( -l $epath )
788
            {
789
                my $lver = readlink( $epath );
790
                if ( ! defined $lver )
791
                {
3846 dpurdie 792
                    $logger->warn("Can't resolve symlink: $pname, $pver");
1038 dpurdie 793
                    next;
794
                }
795
 
796
                if ( $lver =~ m ~/~ )
797
                {
798
                    $logger->warn("Won't resolve symlink: $pname, $pver, $lver");
799
                    next;
800
                }
6098 dpurdie 801
 
6475 dpurdie 802
                #
803
                #   Add the package the link points to
804
                #
805
                $logger->verbose2("Add linked package: $pname, $lver, $pdata");
806
                $pkgList->{$pname}{$lver} = $pdata;
7389 dpurdie 807
 
808
                if ($isS3Target) {
809
                    $logger->verbose2("Won't send symlink to S3: $pname, $pver, $lver");
810
                    next;
811
                }
812
 
1038 dpurdie 813
            }
814
 
815
            $logger->verbose2("Add extra package: $pname, $pver, $pdata");
816
            $pkgList->{$pname}{$pver} = $pdata;
817
        }
818
    }
6475 dpurdie 819
#Utils::DebugDumpData ("parsePkgList", $rv);
7389 dpurdie 820
#Utils::DebugDumpData ("remotePkgList", $remotePkgList);
1038 dpurdie 821
 
6475 dpurdie 822
 
1040 dpurdie 823
    #
824
    #   If there are no packages to process, then assume that this is an error
825
    #   condition. Retry the operation soon.
826
    #
827
    unless ( keys %{$pkgList} )
828
    {
829
 
830
        $logger->verbose2("No packages to process");
5398 dpurdie 831
        $statistics{state} = 'No Packages found';
1040 dpurdie 832
        return;
833
    }
834
 
6475 dpurdie 835
#   #
836
#   #   Useful debug code
837
#   #
838
#   while ( (my ($pname, $pvers)) = each %{$pkgList} )
839
#   {
840
#       while ( (my ($pver, $ptime) ) = each %{$pvers} )
841
#       {
842
#           print "L-- $pname, $pver, $ptime \n";
1038 dpurdie 843
#
6475 dpurdie 844
#       }
845
#   }
1038 dpurdie 846
 
847
    #
1040 dpurdie 848
    #   Delete Excess Packages
1038 dpurdie 849
    #       Packages not required on the target
1042 dpurdie 850
    #           KLUDGE: Don't delete links to packages
851
    #           Don't delete packages marked for deletion
1038 dpurdie 852
    #
853
    my $excessPkgList;
1048 dpurdie 854
    my $excessPkgListCount = 0;
1038 dpurdie 855
    if ( $conf->{deletePackages} )
856
    {
857
        while ( (my ($pname, $pvers)) = each %{$remotePkgList} )
858
        {
859
            while ( (my ($pver, $pdata) ) = each %{$pvers} )
860
            {
861
                if ( !exists $pkgList->{$pname}{$pver} )
862
                {
1040 dpurdie 863
                    if ( exists $excludePkgs->{$pname} )
864
                    {
865
                        $logger->verbose2("Keep Excluded package: ${pname}");
866
                        next;
867
                    }
868
 
1042 dpurdie 869
                    if ( exists $pdata->{deleted} )
870
                    {
871
                        if ( $conf->{deleteAge} )
872
                        {
873
                            if ( $pdata->{deleted} <= $conf->{deleteAge} )
874
                            {
875
                                $logger->verbose2("Already marked for future age deletion: ${pname}/${pver}, $pdata->{deleted}");
876
                                next;
877
                            }
878
                            $pdata->{FORCEDELETE} = 1;
879
                        }
880
 
881
                        if ( !$conf->{deleteImmediate} )
882
                        {
883
                            $logger->verbose2("Already marked for deletion: ${pname}/${pver}");
884
                            next;
885
                        }
886
                    }
887
 
888
                    #
889
                    #   Force deletion
890
                    #       deleteImmediate mode
891
                    #       target is a broken link
892
                    #
893
                    $pdata->{FORCEDELETE} = 1
894
                        if ($conf->{deleteImmediate} || $pdata->{broken});
895
 
1038 dpurdie 896
                    $excessPkgList->{$pname}{$pver} = $pdata;
1048 dpurdie 897
                    $excessPkgListCount++;
1038 dpurdie 898
                    $logger->verbose("Excess package: ${pname}/${pver}");
899
                }
1050 dpurdie 900
#                else
901
#                {
902
#                        $logger->verbose3("Retain package: ${pname}/${pver}");
903
#                }
1038 dpurdie 904
            }
905
        }
906
    }
907
 
908
    #
909
    #   Process the remote list and the local list
910
    #   The remote time-stamp is the modification time of the packages descpkg file
911
    #
912
    #   Mark for transfer packages that
1040 dpurdie 913
    #       Are in the local set but not the remote set
1038 dpurdie 914
    #       Have a different time stamp
915
    #
916
    #   Ignore packages not in the local archive
917
    #   Ignore packages that don't have a descpkg
918
    #   Ignore packages that are writable - still being formed
919
    #
920
    my $needPkgList;
1048 dpurdie 921
    my $needPkgListCount = 0;
922
    my $filteredCount = 0;
923
    my $missingCount = 0;
924
    my $writableCount = 0;
925
    my $excludeCount = 0;
5404 dpurdie 926
    my $packageVersionCount = 0;
1038 dpurdie 927
    while ( (my ($pname, $pvers)) = each %{$pkgList} )
928
    {
1040 dpurdie 929
        #
930
        #   Ignore excluded packages
931
        #
1048 dpurdie 932
        if ( exists $excludePkgs->{$pname} )
933
        {
934
            $excludeCount++;
935
            next;
936
        }
1040 dpurdie 937
 
1048 dpurdie 938
        #
939
        #   Ignore packages that are filtered out
940
        #
941
        if ( defined $conf->{'packageFilter'} )
942
        {
943
            unless ( $pname =~ m~$conf->{'packageFilter'}~ )
944
            {
945
                $logger->verbose3("Filtering out: ${pname}");
946
                $filteredCount++;
947
                next;
948
            }
949
        }
950
 
1038 dpurdie 951
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
952
        {
6475 dpurdie 953
            my $must_transfer;
954
            my $existsRemote = exists($remotePkgList->{$pname}) && exists ($remotePkgList->{$pname}{$pver});
955
 
6148 dpurdie 956
            #
957
            #   Take care not to create an entry into $remotePkgList->{$pname}{$pver}
958
            #   if it does not exist. Existence of {$pname}{$pver} is used later
959
            #
7389 dpurdie 960
            my $tmtime = undef;
6475 dpurdie 961
            if ($existsRemote && exists ($remotePkgList->{$pname}{$pver}{time})) {
6148 dpurdie 962
                $tmtime = $remotePkgList->{$pname}{$pver}{time};
963
            }
5404 dpurdie 964
            $packageVersionCount++;
1038 dpurdie 965
 
966
            # Package is present in both list
6475 dpurdie 967
            my $localPackage = catdir( $conf->{'dpkg_archive'} , $pname, $pver );
968
            my ($mtime, $mode) = Utils::mtime( catfile($localPackage, 'descpkg') );
1038 dpurdie 969
            if ( $mtime == 0 )
970
            {
971
                # PackageVersion not in local archive (at least the descpkg file is not)
972
                # Skip now - will pick it up later
973
                $logger->verbose("Package not in dpkg_archive: $pname, $pver");
1048 dpurdie 974
                $missingCount++;
1038 dpurdie 975
                next;
976
            }
977
 
978
            if ( $mode & 0222 )
979
            {
980
                # Descpkg file is writable
981
                # Package may be in the process of being created
1048 dpurdie 982
                # If the package has been writable for a long time, then
1038 dpurdie 983
                # consider for transfer
984
                my $age = $now - $mtime;
985
                if ( $age < ($conf->{'writewindow '} || 600) )
986
                {
987
                    $logger->verbose("Package is writable: $pname, $pver, ", $now - $mtime);
1048 dpurdie 988
                    $writableCount++;
1038 dpurdie 989
                    next;
990
                }
991
            }
992
 
7389 dpurdie 993
            if (! $existsRemote ) {
994
                #
995
                #   Package does not exist in the remote, and is eligible for transfer
996
                #
997
                $logger->verbose("Package Needs to be transferred: $pname, $pver");
998
                $must_transfer = 1;
999
 
1000
            } elsif ( defined $tmtime && ($mtime != $tmtime) ) {
1001
                #
1002
                #   Package exists in both source and target
1003
                #   If the package-time is known, then ensure that they are the same
1004
                #
6475 dpurdie 1005
                $logger->verbose("Package Needs to be transferred: $pname, $pver, $mtime, $tmtime");
1006
                $must_transfer = 1;
1007
            }
7389 dpurdie 1008
            else
1038 dpurdie 1009
            {
6475 dpurdie 1010
                #
1011
                #   Package exists in both source and target
1012
                #   Symlink test: Ensure symlinks are the same
1013
                #
1014
                my $localIsSymlink = -l $localPackage;
1015
                my $remoteIsSymlink = exists($remotePkgList->{$pname}) && exists ($remotePkgList->{$pname}{$pver}) && exists ($remotePkgList->{$pname}{$pver}{link});
1016
 
1017
                if ($remoteIsSymlink && $localIsSymlink) {
1018
                    #
1019
                    #   Both are symlinks - check that they address the same item
1020
                    #
1021
                    my $targetLink = $remotePkgList->{$pname}{$pver}{link};
1022
                    $logger->verbose2("Package is symlink: $pname, $pver -> $targetLink");
1023
 
1024
                    my $lver = readlink( $localPackage );
1025
                    if ( ! defined $lver ) {
1026
                        $logger->warn("Can't resolve symlink: $pname, $pver");
1027
                        next;
1028
                    }
1029
                    if ($targetLink ne $lver ) {
1030
                        $logger->verbose("Package symlinks differ: $pname, $pver, $targetLink, $lver");
1031
                        $must_transfer = 3;
1032
                    }
1033
 
1034
                } elsif ($remoteIsSymlink || $localIsSymlink ) {
1035
                    #
1036
                    #   Only one is a symlink - force transfer
1037
                    #
1038
                    $logger->warn("Packages versions not both symlink: $pname, $pver, L:$remoteIsSymlink R:$localIsSymlink");
1039
                    $must_transfer = 2;
1040
                }
1041
            }
1042
 
1043
            #
1044
            #   If we are forcing a package transfer then flag it and also remove it from the
1045
            #   RemotePkgList so that it will be transferred - even if its present on target
1046
            #
1047
            if ($must_transfer) {
1038 dpurdie 1048
                # Package not present on target, or timestamps differ
1049
                $needPkgList->{$pname}{$pver} = $pdata;
6475 dpurdie 1050
                delete $RemotePkgList->{$pname}{$pver};
1048 dpurdie 1051
                $needPkgListCount++;
1038 dpurdie 1052
                next;
1053
            }
1054
        }
1055
    }
1056
 
1048 dpurdie 1057
 
1038 dpurdie 1058
    #
1059
    #   Debug output only
1060
    #   Display what we need to transfer
1061
    #
1062
    if ( $conf->{verbose} > 2 )
1063
    {
1064
        while ( (my ($pname, $pvers)) = each %{$needPkgList} )
1065
        {
1066
            while ( (my ($pver, $pdata) ) = each %{$pvers} )
1067
            {
1068
                $logger->verbose("Need to transfer: $pname, $pver, $pdata");
1069
            }
1070
        }
1071
    }
1048 dpurdie 1072
    if ( $conf->{verbose}  )
1073
    {
1074
        $logger->verbose("Packages to transfer: $needPkgListCount");
1075
        $logger->verbose("Packages to delete: $excessPkgListCount");
1076
        $logger->verbose("Packages filtered out: $filteredCount");
1077
        $logger->verbose("Packages missing: $missingCount");
1078
        $logger->verbose("Packages still writable: $writableCount");
1079
        $logger->verbose("Packages excluded: $excludeCount");
1080
    }
1038 dpurdie 1081
 
1082
    #
5398 dpurdie 1083
    #   Update stats
1084
    #   At this point we are looking pretty good
1085
    #   
1086
    $statistics{state} = 'OK';
5404 dpurdie 1087
    $statistics{total} = $packageVersionCount;
5398 dpurdie 1088
    $statistics{transfer} = $needPkgListCount;
1089
    $statistics{delete} = $excessPkgListCount;
1090
    $statistics{filtered} = $filteredCount;
1091
    $statistics{missing} = $missingCount;
1092
    $statistics{writable} = $writableCount;
1093
    $statistics{excluded} = $excludeCount;
1094
 
1095
    #
1038 dpurdie 1096
    #   Time to do the real work
1097
    #   Transfer packages and delete excess packages
1098
    #   Note: Perform the transfers first
1099
    #         Limit the number of packages processed in one pass
1100
    #
1101
    my $txcount = $conf->{maxpackages};
1102
 
1103
    #
1104
    #   Transfer packages that we have identified
1105
    #
1106
    while ( (my ($pname, $pvers)) = each %{$needPkgList} )
1107
    {
1108
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
1109
        {
7394 dpurdie 1110
            tagForTransfer($pname, $pver);
1048 dpurdie 1111
            $needPkgListCount--;
1038 dpurdie 1112
        }
1113
    }
1114
 
1115
    #
1116
    #   Delete packages that have been identified as excess
1117
    #
1118
    while ( (my ($pname, $pvers)) = each %{$excessPkgList} )
1119
    {
1120
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
1121
        {
7394 dpurdie 1122
            tagForDelete ($pname, $pver, $pdata);
1123
            $excessPkgListCount--;
1124
        }
1125
    }
1126
 
1127
    #
1128
    #   Need to transmission remove tags for packages that don't need any more
1129
    #   ie: Tags that have since been made unnessesary
1130
    #
1131
    my $taggedPackages = getTaggedPackages();
1132
    if ($taggedPackages) {
1133
        #
1134
        #   Mark entries thay we still need
1135
        #
1136
        foreach my $pname ( keys %{$pkgList} )
1137
        {
1138
            foreach my $pver ( keys %{$pkgList->{$pname}} )
1038 dpurdie 1139
            {
7394 dpurdie 1140
                if (exists $taggedPackages->{$pname}{$pver}) {
1141
                    $taggedPackages->{$pname}{$pver} = 1;
1142
                }
1038 dpurdie 1143
            }
7394 dpurdie 1144
        }
1289 dpurdie 1145
 
7394 dpurdie 1146
        #
1147
        #   Sweep out entries that we don't need any more
1148
        foreach my $pname ( keys %{$taggedPackages} ) {
1149
            foreach my $pver ( keys %{$taggedPackages->{$pname}} ) {
1150
                unless ($taggedPackages->{$pname}{$pver}) {
1151
$logger->verbose("Untag: $pname $pver");
1152
                    unTagForTransfer($pname, $pver);
1153
                }
1289 dpurdie 1154
            }
1038 dpurdie 1155
        }
7394 dpurdie 1156
 
1038 dpurdie 1157
    }
7394 dpurdie 1158
 
1038 dpurdie 1159
    #
1160
    #   Send package list to the target
1161
    #
1162
    sendPackageList ($pkgList);
1040 dpurdie 1163
 
1164
    #
1165
    #   On a successful transfer
1166
    #       Force tag processing
1167
    #       Set scan Mode to normal
1168
    #
1169
    $tagDirTime = 0;
1170
    $releaseScanMode = 0;
1038 dpurdie 1171
}
1172
 
1173
#-------------------------------------------------------------------------------
7394 dpurdie 1174
# Function        : tagForTransfer  
1175
#
1176
# Description     : Tag a package to be transferred
1177
#
1178
# Inputs          : $pname
1179
#                   $pvers 
1180
#
1181
# Returns         : 
1182
#
1183
sub tagForTransfer
1184
{
1185
    my ($pname, $pver) = @_;
1186
    my $tag = "$pname::$pver";
1187
    my $myTag =  catfile($conf->{'tagdir'} , $tag);
1188
    unless (-f $myTag ) {
1189
        $logger->verbose2("tagForTransfer: $pname, $pver");
1190
        Utils::TouchFile($conf, $myTag);
1191
    }
1192
    unlink 'DELD::' . $myTag;
1193
    unlink 'DELF::' . $myTag;
1194
}
1195
 
1196
#-------------------------------------------------------------------------------
1197
# Function        : unTagForTransfer  
1198
#
1199
# Description     : Un Tag a package to be transferred
1200
#
1201
# Inputs          : $pname
1202
#                   $pvers 
1203
#
1204
# Returns         : 
1205
#
1206
sub unTagForTransfer
1207
{
1208
    my ($pname, $pver) = @_;
1209
    my $tag = "$pname::$pver";
1210
    my $myTag =  catfile($conf->{'tagdir'} , $tag);
1211
    if (-f $myTag ) {
1212
        $logger->verbose2("UnTagForTransfer: $pname, $pver");
1213
        unlink($myTag);
1214
    }
1215
}
1216
 
1217
#-------------------------------------------------------------------------------
1218
# Function        : tagForDelete  
1219
#
1220
# Description     : Tag a package to be transferred
1221
#                   Generate tags of the form 
1222
#                       DELD::pname::pver   - Delayed Delete
1223
#                       DELF::pname::pver   - Forced Delete
1224
#
1225
# Inputs          : $pname
1226
#                   $pver
1227
#                   $pdata  - Type of delete
1228
#
1229
# Returns         : 
1230
#
1231
sub tagForDelete
1232
{
1233
    my ($pname, $pver, $pdata) = @_;
1234
    my $tag = "$pname::$pver";
1235
    my $myTag =  catfile($conf->{'tagdir'} , $tag);
1236
    my $delType = $pdata->{FORCEDELETE}  ? 'D' : 'F';
1237
    my $myDelTag =  'DEL' . $delType . '::' . $myTag;
1238
 
1239
    unless (-f $myDelTag ) {
1240
        $logger->verbose2("tagForDelete: $pname, $pver");
1241
        Utils::TouchFile($conf, $myDelTag);
1242
    }
1243
    unlink $myTag;
1244
}
1245
 
1246
#-------------------------------------------------------------------------------
1247
# Function        : getTaggedPackages  
1248
#
1249
# Description     : Determine the tagged packages 
1250
#
1251
# Inputs          : None
1252
#
1253
# Returns         : Returns a pointer to a hash of tagged packages of the form
1254
#
1255
sub getTaggedPackages
1256
{
1257
    my $taggedPackages = {};
1258
    foreach ( glob ("$conf->{'tagdir'}/*::*")) 
1259
    {
1260
        next if m~/DEL.::~;
1261
        m~.*/(.*)::(.*)~;
1262
        $taggedPackages->{$1}{$2} = 0;
1263
    }
1264
    return $taggedPackages; 
1265
}
1266
 
1267
 
1268
#-------------------------------------------------------------------------------
1038 dpurdie 1269
# Function        : sendPackageList
1270
#
1271
# Description     : Transfer package list to the target
1272
#
1273
# Inputs          : $pkgList            - Ref to hash of package names and versions
1274
#
1040 dpurdie 1275
# Returns         : Nothing
1276
#                   Don't really care about any errors from this process
1277
#                   Its not essential
1038 dpurdie 1278
#
1279
sub sendPackageList
1280
{
7389 dpurdie 1281
    return if $isS3Target;
1282
 
1038 dpurdie 1283
    my ($pkgList) = @_;
1284
    my ($fh, $filename) = tempfile( "/tmp/blat.$$.XXXX", SUFFIX => '.txt');
1285
    $logger->verbose("sendPackageList:TmpFile: $filename");
6776 dpurdie 1286
 
1287
    return if $conf->{'noTransfers'};
1038 dpurdie 1288
 
1289
    #
1290
    #   Create a temp file with data
1291
    #
1292
    foreach my $pname ( sort keys %{$pkgList} )
1293
    {
1294
        foreach my $pver ( sort keys %{$pkgList->{$pname}} )
1295
        {
1296
            print $fh "$pname/$pver\n";
1297
        }
1298
    }
1299
    close $fh;
1300
 
1301
    #
1302
    #   Transfer to target
1303
    #   Create the process pipe to transfer the file
1304
    #   gzip the file and pipe the result through a ssh session to the target machine
3515 dpurdie 1305
    #   gzip -c filename |  ssh  ... "./receive_file filename"
1038 dpurdie 1306
    #
1307
    my $ph;
3515 dpurdie 1308
    my $gzip_cmd = "$gzip --no-name -c \"$filename\"";
1040 dpurdie 1309
    my $tgt_cmd = "$conf->{'bindir'}/receive_file \"ArchiveList\"";
3515 dpurdie 1310
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 1311
 
1312
    $logger->verbose2("sendPackageList:gzip_cmd:$gzip_cmd");
1313
    $logger->verbose2("sendPackageList:tgt_cmd:$tgt_cmd");
1314
    $logger->verbose2("sendPackageList:ssh_cmd:$ssh_cmd");
1315
 
1316
    open ($ph, "$gzip_cmd | $ssh_cmd |");
1317
    while ( <$ph> )
1318
    {
1319
        chomp;
1320
        $logger->verbose2("sendPackageList:Data: $_");
1321
    }
1322
    close ($ph);
3515 dpurdie 1323
    unlink $filename;
1038 dpurdie 1324
    $logger->verbose("sendPackageList:End: $?");
3515 dpurdie 1325
    LogTxError ($?);
1038 dpurdie 1326
}
1327
 
1328
 
1329
#-------------------------------------------------------------------------------
1330
# Function        : getPkgList
1331
#
1332
# Description     : Determine a set of package versions within the list
1333
#                   of provided releases
1334
#
1335
# Inputs          : @rlist              - A list of releases to examine
1336
#
1337
# Returns         : Ref to a hask of package versions
1338
#
1339
sub getPkgList
1340
{
1341
    my %pdata;
1342
    my $RM_DB;
1343
    connectRM(\$RM_DB);
1344
    $logger->verbose("getPkgList");
1345
 
1346
    #
1347
    #   Determine the releases that are in this project
1348
    #   Build up an sql query
1349
    #
6776 dpurdie 1350
    my $m_rlist = join ',', @_;
1048 dpurdie 1351
    my $m_sqlstr = "SELECT DISTINCT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION, pv.IS_DEPLOYABLE" .
1038 dpurdie 1352
                    " FROM RELEASE_MANAGER.RELEASE_CONTENT rc, RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg" .
6776 dpurdie 1353
                    " WHERE ( RTAG_ID in ($m_rlist) ) AND rc.PV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID" .
1038 dpurdie 1354
                    " ORDER by PKG_NAME DESC";
1355
    $logger->verbose3("getPkgList:Sql:$m_sqlstr");
1356
 
1357
    my $sth = $RM_DB->prepare($m_sqlstr);
1358
    if ( defined($sth) )
1359
    {
1360
        if ( $sth->execute( ) )
1361
        {
1362
            if ( $sth->rows )
1363
            {
1364
                while (my @row = $sth->fetchrow_array )
1365
                {
1366
                    $logger->verbose2("getPkgList:Data:@row");
1367
                    $pdata{$row[1]}{$row[2]} = 1;
1368
                }
1369
            }
1370
            $sth->finish();
1371
        }
1372
    }
1373
    else
1374
    {
1375
        $logger->warn("getPkgList: SQL Prepare failure");
1376
    }
1050 dpurdie 1377
 
1378
   disconnectRM(\$RM_DB);
1038 dpurdie 1379
   return \%pdata;
1380
}
1381
 
1382
 
1383
#-------------------------------------------------------------------------------
1384
# Function        : getReleaseList
1385
#
1386
# Description     : Determine the list of releases to be proccessed
1040 dpurdie 1387
#                   From:
1388
#                       Convert projects to a list of releases
1389
#                       Configured list of releases
1038 dpurdie 1390
#
1391
# Inputs          : None
1392
#
1393
# Returns         : A list of releases to be processed
1394
#
1395
sub getReleaseList
1396
{
1397
    my $RM_DB;
1398
    my %rlist;
1048 dpurdie 1399
    my $m_sqlstr;
1038 dpurdie 1400
    $logger->verbose("getReleaseList");
1401
 
1402
    #
1040 dpurdie 1403
    #   Cache data
1404
    #   Only for one cycle of the main loop
1405
    #
1406
    if ( exists $releaseData{getReleaseList} )
1407
    {
1408
        $logger->verbose3("getReleaseList:Cache hit");
1409
        return @{$releaseData{getReleaseList}};
1410
    }
1411
 
1412
    #
1048 dpurdie 1413
    #   All projects
1038 dpurdie 1414
    #
1048 dpurdie 1415
    if ( $conf->{'allProjects'} )
1038 dpurdie 1416
    {
1048 dpurdie 1417
        $m_sqlstr = "SELECT rt.RTAG_ID" .
1418
                    " FROM RELEASE_MANAGER.RELEASE_TAGS rt" .
1050 dpurdie 1419
                    " WHERE rt.OFFICIAL != 'A'";
1420
                    #" AND rt.OFFICIAL != 'Y'";
1048 dpurdie 1421
    }
1422
    else
1423
    {
1038 dpurdie 1424
        #
1048 dpurdie 1425
        #   Convert list of projects into a list of releases
1038 dpurdie 1426
        #
6776 dpurdie 1427
        if ( @projectList )
1048 dpurdie 1428
        {
1429
            #
1430
            #   Determine the releases that are in this project
1431
            #   Build up an sql query
1432
            #
6776 dpurdie 1433
            my $m_plist = join ',', @projectList;
1048 dpurdie 1434
            $m_sqlstr = "SELECT rt.RTAG_ID" .
1435
                        " FROM RELEASE_MANAGER.RELEASE_TAGS rt" .
6776 dpurdie 1436
                        " WHERE ( PROJ_ID in ( $m_plist) ) AND rt.OFFICIAL != 'A'";
5300 dpurdie 1437
                        #" AND rt.OFFICIAL != 'Y'";
1048 dpurdie 1438
        }
1439
    }
1038 dpurdie 1440
 
1048 dpurdie 1441
    if ( defined $m_sqlstr )
1442
    {
1038 dpurdie 1443
        $logger->verbose3("getReleaseList:Sql:$m_sqlstr");
1048 dpurdie 1444
        connectRM(\$RM_DB);
1038 dpurdie 1445
        my $sth = $RM_DB->prepare($m_sqlstr);
1446
        if ( defined($sth) )
1447
        {
1448
            if ( $sth->execute( ) )
1449
            {
1450
                if ( $sth->rows )
1451
                {
1452
                    while (my @row = $sth->fetchrow_array )
1453
                    {
1454
                        $logger->verbose2("getReleaseList:Data:@row");
1455
                        $rlist{$row[0]} = 1;
1456
                    }
1457
                }
1458
                $sth->finish();
6776 dpurdie 1459
            } else {
1460
                $logger->warn("getReleaseList: SQL Execute failure");
1038 dpurdie 1461
            }
1462
        }
1463
        else
1464
        {
1465
            $logger->warn("getReleaseList: SQL Prepare failure");
1466
        }
1050 dpurdie 1467
        disconnectRM(\$RM_DB);
1038 dpurdie 1468
    }
1469
 
1470
    #
1471
    #   Add in the user specified list of releases
1472
    #
6776 dpurdie 1473
    $rlist{$_} = 1 foreach(@releaseList);
1038 dpurdie 1474
 
1475
    #
1476
    #   Sort for pretty display only
1477
    #
1040 dpurdie 1478
    @{$releaseData{getReleaseList}} = sort {$a <=> $b} keys %rlist;
1479
 
1480
    return @{$releaseData{getReleaseList}};
1038 dpurdie 1481
}
1482
 
1040 dpurdie 1483
#-------------------------------------------------------------------------------
3846 dpurdie 1484
# Function        : getPackageVersions
1485
#
1486
# Description     : Get the list of package-versions available in the package
1487
#                   store.
1488
#
1489
# Inputs          : pkgName             - The package name
1490
#
1491
# Returns         : Array of versions
1492
#
1493
sub getPackageVersions
1494
{
1495
    my ($pkgName) = @_;
1496
    my @versionList;
1497
 
1498
    my $pkgDir = catfile($conf->{'dpkg_archive'} , $pkgName );
1499
    my $dh;
1500
 
1501
    unless (opendir($dh, $pkgDir))
1502
    {
1503
        $logger->warn ("Can't opendir $pkgDir: $!");
1504
        return @versionList;
1505
    }
1506
 
1507
    #
1508
    #   Process each entry
1509
    #   Ignore those that start with a .
1510
    #
1511
    while (my $version = readdir($dh) )
1512
    {
1513
        next if ( $version =~ m~^\.~ );
1514
        my $file = catfile($pkgDir, $version);
1515
 
1516
        next unless ( -d $file );
1517
 
1518
        push @versionList, $version;
1519
        $logger->verbose3("getPackageVersions: $pkgName, $version");
1520
    }
1521
    closedir $dh;
1522
    return @versionList;
1523
}
1524
 
1525
#-------------------------------------------------------------------------------
4456 dpurdie 1526
# Function        : getArchiveList 
1527
#
1528
# Description     : Get the entire set of package versions in the archive
1529
#
1530
# Inputs          : 
1531
#
6475 dpurdie 1532
# Returns         : Ref to a hash of package versions
4456 dpurdie 1533
#
1534
sub getArchiveList
1535
{
1536
    my $pkgDir = $conf->{'dpkg_archive'};
1537
    my %archiveList;
1538
    my $dh;
1539
    my @pkgList;
1540
 
1541
    unless (opendir($dh, $pkgDir))
1542
    {
1543
        $logger->warn ("Can't opendir $pkgDir: $!");
1544
        return \%archiveList;
1545
    }
1546
 
1547
    #
1548
    #   Process each entry
1549
    #   Ignore those that start with a .
1550
    #   Ignore files
1551
    #
1552
    while (my $pkgName = readdir($dh) )
1553
    {
1554
        next if ( $pkgName =~ m~^\.~ );
1555
        my $file = catfile($pkgDir, $pkgName);
1556
 
1557
        next unless ( -d $file );
1558
        $logger->verbose3("getArchiveList: $pkgName");
1559
        push @pkgList, $pkgName;
1560
    }
1561
    closedir $dh;
1562
 
1563
    #   Now get the package versions
1564
    #       Sort for pretty display
1565
    foreach my $pname (sort @pkgList)
1566
    {
1567
        foreach my $pver (getPackageVersions($pname))
1568
        {
1569
            $archiveList{$pname}{$pver} = 1;
1570
        }
1571
    }
1572
 
1573
    return \%archiveList;
1574
}
1575
 
1576
#-------------------------------------------------------------------------------
6320 dpurdie 1577
# Function        : getBlatBin  
1578
#
1579
# Description     : Get the list of files that should be in the targetbin directory
1580
#
1581
# Inputs          : Nothing 
1582
#
1583
# Returns         : A hash of data 
1584
#
1585
sub getBlatBin
1586
{
1587
    my $data;
1588
    $logger->verbose("getBlatBin: $targetBinDir");
1589
    if (opendir(DIR, $targetBinDir ) ) {
1590
        my @vlist = readdir(DIR);
1591
        closedir DIR;
1592
 
1593
        foreach my $vname ( sort @vlist )
1594
        {
1595
            next if ( $vname eq '.' );
1596
            next if ( $vname eq '..' );
1597
            next unless ( -f "$targetBinDir/$vname" );
1598
 
1599
            if (open FILE, "$targetBinDir/$vname") {
1600
                $data->{$vname} = Digest::MD5->new->addfile(*FILE)->hexdigest;
1601
                close (FILE);
1602
            }
1603
        }
1604
    } else {
1605
        $logger->warn("BlatBin Not Found: $targetBinDir");
1606
    }
1607
    return $data;
1608
}
1609
 
1610
 
1611
#-------------------------------------------------------------------------------
1040 dpurdie 1612
# Function        : maintainTagList
1613
#
1614
# Description     : Maintain a data structure for the maintenance of the
1615
#                   tags directory
1616
#
1617
# Inputs          : None
1618
#
1619
# Returns         : Nothing
1620
#
1621
sub maintainTagList
1622
{
1623
    #
1624
    #   Time to perform the scan
1625
    #   Will do at startup and every time period there after
1626
    #
6779 dpurdie 1627
    return unless ( $now > ($lastTagListUpdate + $conf->{tagListUpdate} ));
1040 dpurdie 1628
    $logger->verbose("maintainTagList");
6779 dpurdie 1629
    $lastTagListUpdate = $now;
1038 dpurdie 1630
 
1040 dpurdie 1631
    #
1632
    #   Get list of things
1633
    #
1634
    my %config;
7394 dpurdie 1635
 
1636
    #
1637
    #   Is Tag Processing active
1638
    #   Can configure blat to disable tag sync
1639
    #
1640
    if ( $conf->{'tagMaxPackages'} > 0 )
4457 dpurdie 1641
    {
7394 dpurdie 1642
 
1643
        if ($conf->{'allArchive'} )
1644
        {
1645
            $config{allArchive} = 1
1646
        }
1647
        elsif ($conf->{'allProjects'} )
1648
        {
1649
            $config{allProjects} = 1;
1650
        }
1651
        else
1652
        {
1653
            %{$config{projects}} = map { $_ => 1 } @projectList;
1654
            %{$config{releases}} = map { $_ => 1 } getReleaseList();
1655
        }
1656
    } else {
1657
        $config{disableTagTx} = 1
4457 dpurdie 1658
    }
1040 dpurdie 1659
 
1660
    #
1661
    #   Save data
1662
    #
1663
    my $dump =  Data::Dumper->new([\%config], [qw(*config)]);
1664
#print $dump->Dump;
1665
#$dump->Reset;
1666
 
1667
    #
1668
    #   Save config data
1669
    #
1670
    my $conf_file = catfile( $conf->{'tagdir'},'.config' );
1671
    $logger->verbose3("maintainTagList: Writting $conf_file");
1672
 
1673
    my $fh;
1674
    open ( $fh, '>', $conf_file ) or $logger->err("Can't create $conf_file: $!");
1675
    print $fh $dump->Dump;
1676
    close $fh;
1677
}
1678
 
1679
 
1038 dpurdie 1680
#-------------------------------------------------------------------------------
1681
# Function        : processTags
1682
#
1683
# Description     : Process tags and send marked package versions to the target
1684
#                       Determine if new tags are present
1685
#                       Process each tag
7394 dpurdie 1686
#                       
1687
#                       Two types of tag
1688
#                           Transfer Requests
1689
#                           Delete Request
1690
#                       Send packages before deleting packages    
1038 dpurdie 1691
#
1692
# Inputs          : None
1693
#
1694
# Returns         : Nothing
1695
#
1696
sub processTags
1697
{
6779 dpurdie 1698
 
1038 dpurdie 1699
    #
1700
    #   Determine if new tags are present by examining the time
1701
    #   that the directory was last modified.
1702
    #
1703
    #   Allow for a forced scan to catch packages that did not transfer
1704
    #   on the first attempt
1705
    #
5398 dpurdie 1706
    my $tagCount = 0;
7394 dpurdie 1707
    my $delCount = 0;
1708
 
1038 dpurdie 1709
    my ($mtime) = Utils::mtime($conf->{'tagdir'} );
1710
    if ( ($mtime > $tagDirTime) || ($now > ($lastDirScan + $conf->{'forcedirscan'})) )
1711
    {
1042 dpurdie 1712
        $logger->verbose2("processTags: ",$conf->{'tagdir'}, $mtime - $tagDirTime, $now - $lastDirScan);
1038 dpurdie 1713
        $tagDirTime = $mtime;
1714
        $lastDirScan = $now;
6779 dpurdie 1715
        my $txcount = $conf->{'tagMaxPackages'};
1038 dpurdie 1716
 
1717
        my $dh;
1718
        unless (opendir($dh, $conf->{'tagdir'}))
1719
        {
1720
            $logger->warn ("can't opendir $conf->{'tagdir'}: $!");
1721
            return;
1722
        }
1723
 
1724
        #
1725
        #   Process each entry
1726
        #   Ignore those that start with a .
1727
        #
6779 dpurdie 1728
        my %tagPkgList;
7394 dpurdie 1729
        my %deleteTags;
1038 dpurdie 1730
        while (my $tag = readdir($dh) )
1731
        {
1732
            next if ( $tag =~ m~^\.~ );
1733
            my $file = "$conf->{'tagdir'}/$tag";
1042 dpurdie 1734
            $logger->verbose3("processTags: $file");
1735
 
1038 dpurdie 1736
            next unless ( -f $file );
1737
            next if ( $tag  eq 'ReleaseList' );
1738
 
7394 dpurdie 1739
            if ( $tag  =~ m~^DEL(.)::(.+)::(.+)~) {
1740
                $deleteTags {$2}{$3}{file} = $file;
1741
                $deleteTags {$2}{$3}{mode} = $1 eq 'F' ? 1 : 0;
1742
                $delCount++;
1743
            }
1744
 
1038 dpurdie 1745
            if ( $tag =~ m~(.+)::(.+)~  )
1746
            {
7394 dpurdie 1747
                $tagPkgList{$1}{$2} = $file;
5398 dpurdie 1748
                $tagCount++;
6779 dpurdie 1749
            }
1750
        }
1751
        $statistics{tagCount} = $tagCount;
7394 dpurdie 1752
        $statistics{tagDelCount} = $delCount;
6779 dpurdie 1753
        closedir $dh;
6098 dpurdie 1754
 
6779 dpurdie 1755
        #
7394 dpurdie 1756
        #   Process delete requests after all transfers have occured
1757
        #   
1758
        unless ($tagCount) {
1759
            delete_pkgs:
1760
            while ( (my ($pname, $pvers)) = each %deleteTags )
1761
            {
1762
                while ( (my ($pver, $pdata) ) = each %{$pvers} )
1763
                {
1764
                    if ( --$txcount <= 0 )
1765
                    {
1766
                        $logger->warn("Max tag transfer count exceeded: $delCount deletion remaining");
1767
                        $tagDirTime = 0;
1768
                        last delete_pkgs;
1769
                    }
1770
 
1771
                    if ( readConfig() )
1772
                    {
1773
                        $logger->warn("Config file changed");
1774
                        $txcount = 0;
1775
                        $tagDirTime = 0;
1776
                        last delete_pkgs;
1777
                    }
1778
 
1779
                    deletePackage ($pname, $pver, $pdata->{mode});
1780
                    unlink $pdata->{file};
1781
                    $delCount--;
1782
                    reapChildren();
1783
                }
1784
            }
1785
        }
1786
 
1787
        #
6779 dpurdie 1788
        #   Process the packages located in the tags area
1789
        #
1790
        send_tags:
1791
        while ( (my ($package, $pvers)) = each %{tagPkgList} )
1792
        {
1793
            while ( (my ($version, $file) ) = each %{$pvers} )
1794
            {
1795
                if ( --$txcount <= 0 )
1796
                {
1797
                    $logger->warn("Max tag transfer count exceeded: $tagCount transfer remaining");
7394 dpurdie 1798
                    $logger->warn("Max tag transfer count exceeded: $delCount deletion remaining");
6779 dpurdie 1799
                    $tagDirTime = 0;
1800
                    last send_tags;
1801
                }
1802
 
1803
                if ( readConfig() )
1804
                {
1805
                    $logger->warn("Config file changed");
1806
                    $txcount = 0;
1807
                    $tagDirTime = 0;
1808
                    last send_tags;
1809
                }
1810
 
7389 dpurdie 1811
#               #
1812
#               #   Don't transfer 'extra' packages
1813
#               #   Removed. It was casuing a tarZip to be triggered, but never transferred
1814
#               #
1815
#               if (exists ($extraPkgs->{$package}) )
1816
#               {
1817
#                   $logger->warn ("Delete excess package tag: $package::$version");
1818
#                   unlink $file;
1819
#
1820
#               } else
1821
                if ( transferPackage( $package, $version )) {
1038 dpurdie 1822
                    unlink $file;
6098 dpurdie 1823
                }
1038 dpurdie 1824
                else
1825
                {
7387 dpurdie 1826
                    if ($conf->{'tagage'} > 0) {
1827
                        my ($mtime) = Utils::mtime( $file );
1828
                        if ( $now - $mtime > $conf->{'tagage'} )
1829
                        {
1830
                            $logger->warn ("Delete unsatisfied tag: $package::$version after $conf->{'tagage'}" );
1831
                            unlink $file;
1832
                            $statistics{staleTags}++;
1833
                        }
1038 dpurdie 1834
                    }
1835
                }
6779 dpurdie 1836
 
1837
                $tagCount--;
1838
                reapChildren();
1038 dpurdie 1839
            }
1840
        }
1841
    }
1842
}
1843
 
1844
#-------------------------------------------------------------------------------
6320 dpurdie 1845
# Function        : transferBlatBin 
1846
#
1847
# Description     : Transfer any of the Blat Bin files that are out of date
1848
#                   on the target
1849
#
1850
# Inputs          : $fileHash       - A hash whose files are those that need
1851
#                                     to be updated 
1852
#
1853
# Returns         : 
1854
#
1855
sub transferBlatBin
1856
{
1857
    my ($hash) = @_;
1858
    $logger->verbose("transferBlatBin");
6776 dpurdie 1859
 
1860
    return if $conf->{'noTransfers'};
1861
 
6320 dpurdie 1862
    foreach my $file ( sort keys %{$hash})
1863
    {
1864
        $logger->logmsg("transferBlatBin: $file");
1865
 
1866
        #
1867
        #   Transfer one file using only 'ssh'
1868
        #   Create the target directory on the fly
1869
        #   Manipulate file permissions
1870
        #   Report errors
1871
 
1872
        my $tar_cmd = "cat \"$targetBinDir/$file\"";
6475 dpurdie 1873
        my $tgt_cmd = "mkdir -p ~/bin && if [ -f \"~/bin/$file\" ] ; then chmod +x+w \"~/bin/$file\"; fi && cat > \"~/bin/$file\" && chmod +x-w \"~/bin/$file\" || exit 1";
6320 dpurdie 1874
        my $ssh_cmd = sshCmd($tgt_cmd);
1875
        my $cat_cmd = 
1876
 
1877
        $logger->verbose2("transferBlatBin:tar_cmd:$tar_cmd");
1878
        $logger->verbose2("transferBlatBin:tgt_cmd:$tgt_cmd");
1879
        $logger->verbose2("transferBlatBin:ssh_cmd:$ssh_cmd");
1880
 
1881
        my $ph;
1882
        open ($ph, "$tar_cmd | $ssh_cmd |");
1883
        while ( <$ph> )
1884
        {
1885
            chomp;
1886
            $logger->verbose2("transferBlatBin:Data: $_");
1887
        }
1888
        close ($ph);
1889
        $logger->verbose("transferBlatBin:End: $?");
1890
 
1891
        if ( $? != 0 )
1892
        {
1893
            $logger->warn("transferBlatBin:Transfer Error: $file, $?");
1894
        }
1895
        LogTxError ($?);
1896
    }
1897
}
1898
 
7394 dpurdie 1899
#-------------------------------------------------------------------------------
1900
# Function        : calcZipRequests 
1901
#
1902
# Description     : Determine the number of packages that I need that have
1903
#                   outstanding ZIP requests
1904
#                   
1905
#                   Purpose is to limit the number of ZIP requests that are
1906
#                   outstanding at any one time to reduce disc space
1907
#                   
1908
#                   ie: Iff there 1000 outstanding transfers, we don't need to zip all
1909
#                   of the packages right now.
1910
#
1911
# Inputs          : 
1912
#
1913
# Returns         : 
1914
#
1915
#
1916
sub calcZipRequests
1917
{
1918
    my $zipRequests = 0;
6320 dpurdie 1919
 
7394 dpurdie 1920
    $conf->{'tagdir'} =~ m~^(.*)/~;
1921
    my $tagRoot = $1;
1922
 
1923
    my $dh;
1924
    if ( opendir($dh, $conf->{'tagdir'}) )
1925
    {
1926
        while (my $tag = readdir($dh) )
1927
        {
1928
            next if ( $tag =~ m~^\.~ );
1929
            my $file = "$conf->{'tagdir'}/$tag";
1930
            $logger->verbose3("calcZipRequests: $file");
1931
 
1932
            next unless ( -f $file );
1933
            next if ( $tag  eq 'ReleaseList' );
1934
            next if ( $tag =~ m~^DEL(.)::~);
1935
 
1936
            my $zipTag = catfile($tagRoot, 'tarZip', $tag);
1937
            if (-f $zipTag ) {
1938
                $zipRequests++;
1939
            }
1940
        }
1941
        closedir $dh;
1942
    }
1943
    $logger->verbose("zipRequests: $zipRequests");
1944
    return $zipRequests;
1945
}
1946
 
1947
 
6320 dpurdie 1948
#-------------------------------------------------------------------------------
1038 dpurdie 1949
# Function        : transferPackage
1950
#
1951
# Description     : Transfer specified package to target system
6475 dpurdie 1952
#                   If a symlink, then a symlink will be transferred
1038 dpurdie 1953
#
1954
# Inputs          : $pname          - Name of the package
1955
#                   $pver           - Package version
1956
#
1957
# Returns         : true    - Package transferred
1958
#                   false   - Package not transferred
1959
#
1960
sub transferPackage
1961
{
6475 dpurdie 1962
    my ($pname, $pver ) = @_;
1038 dpurdie 1963
    my $rv = 0;
6776 dpurdie 1964
    my $cmdRv = 0;
7389 dpurdie 1965
    $logger->verbose("Enter transferPackage: @_");
6148 dpurdie 1966
    my $startTime = time;
1038 dpurdie 1967
 
1968
    #
1040 dpurdie 1969
    #   Do not transfer excluded files
1970
    #
1971
    if ( exists $excludePkgs->{$pname} )
1972
    {
1048 dpurdie 1973
        $logger->warn("transferPackage: Excluded package not transferred: $pname, $pver");
1040 dpurdie 1974
        return 1;
1975
    }
1048 dpurdie 1976
 
1040 dpurdie 1977
    #
1048 dpurdie 1978
    #   Apply package filter
1979
    #
1980
    if ( defined $conf->{'packageFilter'} )
1981
    {
1982
        unless ( $pname =~ m~$conf->{'packageFilter'}~ )
1983
        {
1984
            $logger->warn("transferPackage: Filtered out package not transferred: $pname, $pver");
1985
            return 1;
1986
        }
1987
    }
1988
 
1989
    #
6148 dpurdie 1990
    #   If its known to be in the target archive, then we don't need to transfer it again
1991
    #       It may have been transferred in this cycle
1992
    #       It may have been in the archive anyway
1038 dpurdie 1993
    #
6148 dpurdie 1994
    if ( exists($RemotePkgList->{$pname}) && exists ($RemotePkgList->{$pname}{$pver})) {
1995
        $logger->verbose("transferPackage: Already in archive");
6320 dpurdie 1996
        #$logger->logmsg("transferPackage: $pname, $pver : Already in archive");
1038 dpurdie 1997
        return 1;
1998
    }
1999
 
6475 dpurdie 2000
    my $sdir = catfile( $conf->{'dpkg_archive'} , $pname );
2001
    my $sfile = catfile( $sdir, $pver );
1038 dpurdie 2002
    unless ( -d $sfile )
2003
    {
2004
        $logger->warn("transferPackage:Package not found: $pname, $pver");
2005
        return $rv;
2006
    }
2007
 
7387 dpurdie 2008
    my $tzdir = catfile( $conf->{'dpkg_archive'} , '.dpkg_archive', 'tarStore' );
2009
    my $tzfile = $pname . '__' . $pver . '.tgz';
2010
    my $tzpath = catfile($tzdir, $tzfile);
2011
    unless (-f $tzpath) {
7394 dpurdie 2012
        $logger->verbose("transferPackage: tarZip not found - $tzfile");
7387 dpurdie 2013
 
2014
        $conf->{'tagdir'} =~ m~^(.*)/~;
2015
        my $tagRoot = $1;
2016
        my $tag = "$pname::$pver";
7389 dpurdie 2017
        my $zipTag = catfile($tagRoot, 'tarZip', $tag);
2018
        my $myTag =  catfile($conf->{'tagdir'} , $tag);
2019
 
2020
        $logger->logmsg("transferPackage. Request Zip: @_") unless ( -f $zipTag && -f $myTag );
7394 dpurdie 2021
        Utils::TouchFile($conf, $myTag) unless -f $myTag;
7389 dpurdie 2022
 
7394 dpurdie 2023
        unless (-f $zipTag) {
2024
            if (calcZipRequests() < $conf->{maxTarZips} ) {
2025
                Utils::TouchFile($conf, $zipTag);
2026
            } else {
2027
                $logger->verbose("transferPackage: Max outstanding tarZip Requests");
2028
            }
2029
        }
7387 dpurdie 2030
        return 0;
2031
    }
2032
 
2033
 
6475 dpurdie 2034
    ###########################################################################
2035
    #   Transfer the package / symlink
1038 dpurdie 2036
    #
7389 dpurdie 2037
    $logger->logmsg("transferPackage: @_");
2038
    my $tzfsize = -s $tzpath; 
7387 dpurdie 2039
    if ($isS3Target) {
2040
        $cmdRv = transferPackageS3($tzdir, $tzfile, $pname, $pver);
2041
    } else {
2042
        $cmdRv = transferPackageSsh($tzdir, $tzfile, $sfile, $pname, $pver);
2043
    }
2044
 
2045
    #
7389 dpurdie 2046
    #   Display the size of the package (tarZipped)
7387 dpurdie 2047
    #       Diagnostic use
2048
    #
2049
    if ($conf->{txdetail}) {
7389 dpurdie 2050
        my $size = sprintf "%.3f", $tzfsize / 1024 / 1024 / 1024 ;
7387 dpurdie 2051
        my $duration = time - $startTime;
2052
        $logger->logmsg("transferPackage: Stats: $pname, $pver, $size Gb, $duration Secs");
2053
    }
2054
 
2055
    if ( $cmdRv == 0 ) {           
2056
        #
2057
        #   Mark has having been transferred in the current cycle
2058
        #
2059
        $RemotePkgList->{$pname}{$pver}{transferred} = 1;
2060
        $rv = 1;
2061
        $statistics{txCount}++;
2062
 
2063
    } else {
2064
        $logger->warn("transferPackage:Transfer Error: $pname, $pver, $?");
2065
    }
2066
 
2067
    LogTxError ($?);
2068
    return $rv;
2069
}
2070
 
2071
#-------------------------------------------------------------------------------
2072
# Function        : transferPackageSsh
2073
#
2074
# Description     : Transfer a package via an ssh connection 
2075
#
2076
# Inputs          : $tzdir  - Directory that contains the tarZip file
2077
#                   $tzname - Name of tarZip File
2078
#                   $sfile  - Full path to the source file
2079
#                   $pname  - Package Name
2080
#                   $pver   - Package Version 
2081
#
2082
# Returns         : Result Code
2083
#                   0   - Transfer OK
2084
#                   <0  - Skip transfer
2085
#                   >0  - Command error code
2086
#
2087
sub transferPackageSsh
2088
{
2089
    my ($tzdir, $tzname, $sfile, $pname, $pver) = @_;
6475 dpurdie 2090
    my $tgt_cmd;
2091
    my $ssh_cmd;
7387 dpurdie 2092
    my $cmdRv = 0;
2093
    my $tzfile = catfile($tzdir, $tzname);
2094
 
6475 dpurdie 2095
    if (-l $sfile) {
1038 dpurdie 2096
 
6475 dpurdie 2097
        #
2098
        #   Determine the value of the symlink
7387 dpurdie 2099
        #   Only support simple symlinks - that are in the same directory
6475 dpurdie 2100
        #
2101
        my $lver = readlink( $sfile );
2102
        if ( ! defined $lver ) {
2103
            $logger->warn("Can't resolve symlink: $pname, $pver");
7387 dpurdie 2104
            return -1;
6475 dpurdie 2105
        }
2106
 
2107
        if ( $lver =~ m ~/~ ) {
2108
            $logger->warn("Won't resolve symlink: $pname, $pver, $lver");
7387 dpurdie 2109
            return -1;
6475 dpurdie 2110
        }
2111
 
2112
        $tgt_cmd = "$conf->{'bindir'}/receive_symlink \"$pname\" \"$pver\" \"$lver\"";
2113
        $ssh_cmd = sshCmd($tgt_cmd);
2114
 
2115
    } else {
2116
        #
2117
        #   Create the process pipe to transfer the package
7387 dpurdie 2118
        #   Pipe the tarZip of the package through a ssh session to the target machine
2119
        #   cat $tzpath | ssh  ... "./receive_package pname pver"
6475 dpurdie 2120
        #
2121
        $tgt_cmd = "$conf->{'bindir'}/receive_package \"$pname\" \"$pver\"";
2122
        $ssh_cmd = sshCmd($tgt_cmd);
7387 dpurdie 2123
        $ssh_cmd .= " <$tzfile"
6475 dpurdie 2124
    }
2125
 
1038 dpurdie 2126
    $logger->verbose2("transferPackage:tgt_cmd:$tgt_cmd");
2127
    $logger->verbose2("transferPackage:ssh_cmd:$ssh_cmd");
2128
 
6776 dpurdie 2129
    unless ($conf->{'noTransfers'}) {
2130
        my $ph;
7387 dpurdie 2131
        open ($ph, "$ssh_cmd |");
6776 dpurdie 2132
        while ( <$ph> )
2133
        {
2134
            chomp;
2135
            $logger->verbose2("transferPackage:Data: $_");
2136
        }
2137
        close ($ph);
2138
        $cmdRv = $?;
2139
        $logger->verbose("transferPackage:End: $?");
1038 dpurdie 2140
    }
6148 dpurdie 2141
 
7387 dpurdie 2142
    return $cmdRv;
2143
}
2144
 
2145
#-------------------------------------------------------------------------------
2146
# Function        : transferPackageS3
2147
#
2148
# Description     : Transfer a package to an AWS S3 bucket
2149
#                   Requires that the package already be tarZip-ed
2150
#
2151
# Inputs          : $tzdir  - Directory that contains the tarZip file
2152
#                   $tzfile - Name of tarZip File
2153
#                   $pname  - Package Name
2154
#                   $pver   - Package Version 
2155
#
2156
# Returns         : Result Code
2157
#                   0   - Transfer OK
2158
#                   <0  - Skip transfer
2159
#                   >0  - Command error code
2160
#
2161
sub transferPackageS3
2162
{
2163
    my ($tzdir, $tzfile, $pname, $pver) = @_;
2164
    my $cmdRv = 0;
2165
 
6148 dpurdie 2166
    #
7387 dpurdie 2167
    #   Locate the file on the dpkgArchive tarZip store
2168
    #   
2169
    my $sfile = catfile($tzdir, $tzfile);
2170
    if (-l $sfile) {
2171
        $logger->warn("Will not transfer symlink: $pname, $pver");
2172
        return -1;
2173
    }
2174
 
6148 dpurdie 2175
    #
7387 dpurdie 2176
    #   Create a command to transfer the file to AWS use the cli tools
2177
    #   Note: Ive seen problem with this when used from Perth to AWS (Sydney)
2178
    #         If this is an issue use curl - see the savePkgToS3.sh for an implementation
2179
    #   
2180
    my $s3_cmd = "aws --profile $conf->{'S3Profile'} s3 cp $sfile s3://$conf->{'S3Bucket'}/$tzfile";
2181
    $logger->verbose2("transferPackage:s3_cmd:$s3_cmd");
2182
 
2183
    unless ($conf->{'noTransfers'}) {
6776 dpurdie 2184
        my $ph;
7387 dpurdie 2185
        open ($ph, "$s3_cmd |");
2186
        while ( <$ph> )
2187
        {
2188
            chomp;
2189
            $logger->verbose2("transferPackage:Data: $_");
2190
        }
2191
        close ($ph);
2192
        $cmdRv = $?;
2193
        $logger->verbose("transferPackage:End: $?");
6475 dpurdie 2194
    }
6148 dpurdie 2195
 
7387 dpurdie 2196
    return $cmdRv;
1038 dpurdie 2197
}
2198
 
7387 dpurdie 2199
 
1038 dpurdie 2200
#-------------------------------------------------------------------------------
2201
# Function        : deletePackage
2202
#
2203
# Description     : Delete specified package to target system
2204
#
2205
# Inputs          : $pname          - Name of the package
2206
#                   $pver           - Package version
7394 dpurdie 2207
#                   $mode           - 1 ForcedDelete 0: Tag for delayed Delete
1038 dpurdie 2208
#
7389 dpurdie 2209
# Returns         : true    - Package deleted
2210
#                   false   - Package not deleted
1038 dpurdie 2211
#
2212
sub deletePackage
2213
{
7394 dpurdie 2214
    my ($pname, $pver, $mode ) = @_;
1038 dpurdie 2215
    my $rv = 0;
6776 dpurdie 2216
    my $cmdRv = 0;
1042 dpurdie 2217
    $logger->logmsg("deletePackage: $pname, $pver");
1038 dpurdie 2218
 
7389 dpurdie 2219
    if ($isS3Target) {
2220
        #   Create the process pipe to delete the package
1038 dpurdie 2221
 
7389 dpurdie 2222
        my $tzfile = $pname . '__' . $pver . '.tgz';
2223
        my $s3_cmd = "aws --profile $conf->{'S3Profile'} s3 rm s3://$conf->{'S3Bucket'}/$tzfile";
2224
        $logger->verbose2("deletePackage:s3_cmd:$s3_cmd");
1038 dpurdie 2225
 
7389 dpurdie 2226
        my $ph;
2227
        open ($ph, "$s3_cmd |");
6776 dpurdie 2228
        while ( <$ph> )
2229
        {
2230
            chomp;
2231
            $logger->verbose2("deletePackage:Data: $_");
2232
        }
2233
        close ($ph);
2234
        $cmdRv = $?;
2235
 
7389 dpurdie 2236
    } else {
2237
        #
2238
        #   Create the process pipe to delete the package
2239
        #   ssh  ... "./delete_package ${rx_opts} \"$pname\" \"$pver\""
2240
        #
2241
        unless ($conf->{'noTransfers'}) {
2242
            my $ph;
7394 dpurdie 2243
            my $flags = $mode  ? '-T' : '';
7389 dpurdie 2244
            my $tgt_cmd = "$conf->{'bindir'}/delete_package $flags \"$pname\" \"$pver\"";
2245
            my $ssh_cmd = sshCmd($tgt_cmd);
2246
 
2247
            $logger->verbose2("deletePackage:tgt_cmd:$tgt_cmd");
2248
            $logger->verbose2("deletePackage:ssh_cmd:$ssh_cmd");
2249
 
2250
            open ($ph, "$ssh_cmd |");
2251
            while ( <$ph> )
2252
            {
2253
                chomp;
2254
                $logger->verbose2("deletePackage:Data: $_");
2255
            }
2256
            close ($ph);
2257
            $cmdRv = $?;
2258
        }
1038 dpurdie 2259
    }
6776 dpurdie 2260
 
7389 dpurdie 2261
    #
2262
    #   Common code
2263
    #
2264
    $logger->verbose("deletePackage:End: $cmdRv");
6776 dpurdie 2265
    if ( $cmdRv == 0 )
1038 dpurdie 2266
    {
2267
        $rv = 1;
5398 dpurdie 2268
        $statistics{delCount}++;
6148 dpurdie 2269
        delete $RemotePkgList->{$pname}{$pver};
1038 dpurdie 2270
    }
2271
    else
2272
    {
2273
        $logger->warn("deletePackage:Error: $pname, $pver, $?");
2274
    }
3515 dpurdie 2275
    LogTxError ($?);
1038 dpurdie 2276
    return $rv;
2277
}
2278
 
3515 dpurdie 2279
#-------------------------------------------------------------------------------
2280
# Function        : sshCmd
2281
#
2282
# Description     : Generate a ssh based command
2283
#
2284
# Inputs          : Target command
2285
#
2286
# Returns         : An shh command string
2287
#
2288
sub sshCmd
2289
{
2290
    my ($tgt_cmd) = @_;
3847 dpurdie 2291
    my $sshPort = '';
2292
    $sshPort = "-p $conf->{'sshport'}"
2293
        if ($conf->{'sshport'});
2294
 
2295
    return "ssh -o \"BatchMode yes\" -i $conf->{'identity'} ${sshPort} $conf->{'user'}\@$conf->{'hostname'} \"$tgt_cmd\" 2>&1";
3515 dpurdie 2296
}
1038 dpurdie 2297
 
3515 dpurdie 2298
 
1038 dpurdie 2299
#-------------------------------------------------------------------------------
1042 dpurdie 2300
# Function        : parsePkgList
2301
#
2302
# Description     : Parse one line from a pkgList
2303
#                   Lines are multiple item="data" items
6148 dpurdie 2304
#                       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas"
6475 dpurdie 2305
#                       time="1497586865" GMT="Fri Jun 16 04:21:05 2017" pname="ERGissaccounts" pver="1.0.7169.mas" link="latest"
1042 dpurdie 2306
#
2307
# Inputs          : $line                   - Line of data
2308
#                   $hashp                  - Ref to hash to populate
2309
#
2310
# Returns         : A hash of data items
2311
#
2312
sub parsePkgList
2313
{
2314
    my ($line, $hashp) = @_;
2315
    my $rv;
2316
 
2317
    while ( $line =~ m~\s*(.+?)="(.+?)"~ )
2318
    {
2319
        $rv->{$1} = $2;
2320
        $line = $';
2321
    }
2322
#Utils::DebugDumpData ("parsePkgList", $rv);
2323
 
2324
    my $pname = $rv->{pname};
2325
    my $pver =  $rv->{pver};
2326
    return undef unless ( $pname && $pver );
2327
 
2328
    delete $rv->{pname};
2329
    delete $rv->{pver};
2330
    delete $rv->{GMT};
2331
 
2332
    $hashp->{$pname}{$pver} = $rv;
2333
    return $hashp;
2334
}
2335
 
5398 dpurdie 2336
#-------------------------------------------------------------------------------
5404 dpurdie 2337
# Function        : parsePkgMetaData
2338
#
2339
# Description     : Parse one line of meta data from a pkgList
2340
#                   Lines are multiple item="data" items
2341
#
2342
# Inputs          : $line                   - Line of data
2343
#                   $hashp                  - Ref to hash to populate
2344
#
2345
# Returns         : Nothing
2346
#
2347
sub parsePkgMetaData
2348
{
2349
    my ($line, $hashp) = @_;
2350
 
2351
    if ( $line =~ m~\s+(.+?)="(.+?)"~ )
2352
    {
2353
        $hashp->{$1} = $2;
2354
        $statistics{'Target.' . $1} = $2;
2355
        $line = $';
2356
 
2357
        $logger->verbose2("parsePkgMetaData: $1 = $2");
2358
    }
2359
}
2360
 
6320 dpurdie 2361
#-------------------------------------------------------------------------------
2362
# Function        : parseBlatBinData
2363
#
2364
# Description     : Parse one line of Blat Bin data from a pkgList
2365
#                   Lines are of the form:
2366
#                   BlatBin MD5="dbc4507f4db5b41f7358b28bce65a15d" file="ddp-gtar"
2367
#
2368
# Inputs          : $line                   - Line of data
2369
#                   $hashp                  - Ref to hash to populate
2370
#
2371
# Returns         : Nothing
2372
#
2373
sub parseBlatBinData
2374
{
2375
    my ($line, $hashp) = @_;
5404 dpurdie 2376
 
6320 dpurdie 2377
    my $rv;
2378
    $line =~ s~^\S+~~;
2379
    while ( $line =~ m~\s*(.+?)="(.+?)"~ )
2380
    {
2381
        $rv->{$1} = $2;
2382
        $line = $';
2383
    }
2384
#Utils::DebugDumpData ("parseBlatBinData", $rv);
2385
 
2386
    my $fname = $rv->{file};
2387
    my $md5 =  $rv->{MD5};
2388
    return undef unless ( $fname && $md5 );
2389
 
2390
    $logger->verbose2("parseBlatBinData: $fname : $md5");
2391
    $hashp->{$fname} = $md5;
2392
}
2393
 
5404 dpurdie 2394
#-------------------------------------------------------------------------------
5398 dpurdie 2395
# Function        : resetDailyStatistics 
2396
#
2397
# Description     : Called periodically to reset the daily statistics
2398
#
2399
# Inputs          : $time       - Current time
2400
#
2401
# Returns         : 
2402
#
2403
sub resetDailyStatistics
2404
{
2405
    my ($time) = @_;
1042 dpurdie 2406
 
5398 dpurdie 2407
    #
2408
    #   Detect a new day
2409
    #
2410
    my $today = (localtime($time))[7];
2411
    if ($yday != $today)
2412
    {
2413
        $yday = $today;
2414
        $logger->logmsg('Resetting daily statistics' );
2415
 
5404 dpurdie 2416
        # Note: Must match @recoverTags in readStatistics
5398 dpurdie 2417
        $statistics{dayStart} = $time;
2418
        $statistics{txCount} = 0;
2419
        $statistics{delCount} = 0;
2420
        $statistics{staleTags} = 0;
2421
        $statistics{linkErrors} = 0;
2422
    }
2423
}
2424
 
1042 dpurdie 2425
#-------------------------------------------------------------------------------
5404 dpurdie 2426
# Function        : readStatistics 
2427
#
2428
# Description     : Read in the last set of stats
2429
#                   Used after a restart to recover daily statistics
2430
#
2431
# Inputs          : 
2432
#
2433
# Returns         : 
2434
#
2435
sub readStatistics
2436
{
2437
    my @recoverTags = qw(dayStart txCount delCount staleTags linkErrors);
2438
 
2439
    if ($conf->{'statsfile'} and -f $conf->{'statsfile'})
2440
    {
2441
        if (open my $fh, $conf->{'statsfile'})
2442
        {
2443
            while (<$fh>)
2444
            {
2445
                m~(.*):(.*)~;
2446
                if ( grep( /^$1$/, @recoverTags ) ) 
2447
                {
2448
                    $statistics{$1} = $2;
6475 dpurdie 2449
                    $logger->verbose("readStatistics $1, $2");
5404 dpurdie 2450
                }
2451
            }
2452
            close $fh;
2453
            $yday = (localtime($statistics{dayStart}))[7];
2454
        }
2455
    }
2456
}
2457
 
2458
 
2459
#-------------------------------------------------------------------------------
5398 dpurdie 2460
# Function        : periodicStatistics 
2461
#
2462
# Description     : Called on a regular basis to write out statistics
2463
#                   Used to feed information into Nagios
2464
#                   
2465
#                   This function is called via an alarm and may be outside the normal
2466
#                   processing loop. Don't make assumptions on the value of $now
2467
#
2468
# Inputs          : 
2469
#
2470
# Returns         : 
2471
#
2472
sub periodicStatistics
2473
{
2474
    #
2475
    #   A few local stats
2476
    #
2477
    $statistics{SeqNum}++;
2478
    $statistics{timeStamp} = time();
2479
    $statistics{upTime} = $statistics{timeStamp} - $startTime;
2480
 
2481
    #   Reset daily accumulations - on first use each day
2482
    resetDailyStatistics($statistics{timeStamp});
2483
 
2484
    #
2485
    #   Write statistics to a file
2486
    #       Write to a tmp file, then rename.
2487
    #       Attempt to make the operation atomic - so that the file consumer
2488
    #       doesn't get a badly formed file.
2489
    #   
2490
    if ($conf->{'statsfiletmp'})
2491
    {
2492
        my $fh;
2493
        unless (open ($fh, '>', $conf->{'statsfiletmp'}))
2494
        {
2495
            $fh = undef;
2496
            $logger->warn("Cannot create temp stats file: $!");
2497
        }
2498
        else
2499
        {
5404 dpurdie 2500
            foreach my $key ( sort { lc($a) cmp lc($b) } keys %statistics)
5398 dpurdie 2501
            {
2502
                print $fh $key . ':' . $statistics{$key} . "\n";
2503
                $logger->verbose2('Statistics:'. $key . ':' . $statistics{$key});
2504
            }
2505
            close $fh;
2506
 
2507
            # Rename temp to real file
2508
            rename  $conf->{'statsfiletmp'},  $conf->{'statsfile'} ;
2509
        }
2510
    }
2511
}
2512
 
2513
#-------------------------------------------------------------------------------
1038 dpurdie 2514
# Function        : sighandlers
2515
#
2516
# Description     : Install signal handlers
2517
#
2518
# Inputs          : $conf           - System config
2519
#
2520
# Returns         : Nothing
2521
#
2522
sub sighandlers
2523
{
5398 dpurdie 2524
    my $conf = shift;
2525
    my $logger = $conf->{logger};
1038 dpurdie 2526
 
5398 dpurdie 2527
    $SIG{TERM} = sub {
2528
        # On shutdown
2529
        $logger->logmsg('Received SIGTERM. Shutting down....' );
2530
        unlink $conf->{'pidfile'} if (-f $conf->{'pidfile'});
2531
        exit 0;
2532
    };
1038 dpurdie 2533
 
5398 dpurdie 2534
    $SIG{HUP} = sub {
2535
        # On logrotate
2536
        $logger->logmsg('Received SIGHUP.');
2537
        $logger->rotatelog();
2538
    };
1038 dpurdie 2539
 
5398 dpurdie 2540
    $SIG{USR1} = sub {
2541
        # On Force Archive Sync
2542
        $logger->logmsg('Received SIGUSR1.');
1046 dpurdie 2543
        $lastReleaseScan = 0;
6779 dpurdie 2544
        $lastTagListUpdate = 0;
6776 dpurdie 2545
        $lastRmConfRead = 0;
5398 dpurdie 2546
    };
1038 dpurdie 2547
 
7387 dpurdie 2548
    alarm 60 unless $conf->{debug};
5398 dpurdie 2549
    $SIG{ALRM} = sub {
2550
        # On Dump Statistics
2551
        $logger->verbose2('Received SIGUSR2.');
2552
        periodicStatistics();
2553
        alarm 60;
2554
    };
2555
 
1038 dpurdie 2556
    $SIG{__WARN__} = sub { $logger->warn("@_") };
2557
    $SIG{__DIE__} = sub { $logger->err("@_") };
2558
}
2559
 
2560
#-------------------------------------------------------------------------------
3515 dpurdie 2561
# Function        : LogTxError
2562
#
2563
# Description     : Detect restoration of communication and log such
2564
#                   Don't log failures as the user will do that
2565
#
2566
# Inputs          : $state                  - 0 - All is well
2567
#                                           !0  - Error
2568
#
2569
# Returns         : Nothing
2570
#
2571
sub LogTxError
2572
{
2573
    my ($state) = $@;
2574
    if ( $state )
2575
    {
5398 dpurdie 2576
        $statistics{linkErrors}++ unless($comError);
3515 dpurdie 2577
        $comError++;
5398 dpurdie 2578
        $statistics{state} = 'No Communication';
3515 dpurdie 2579
    }
2580
    elsif ( $comError )
2581
    {
2582
        $logger->warn("Communication Restored");
2583
        $comError = 0;
5398 dpurdie 2584
        $statistics{state} = 'OK';
3515 dpurdie 2585
    }
2586
}
2587
 
2588
 
2589
#-------------------------------------------------------------------------------
1038 dpurdie 2590
# Function        : Error, Verbose, Warning
2591
#
2592
# Description     : Support for JatsRmApi
2593
#
2594
# Inputs          : Message
2595
#
2596
# Returns         : Nothing
2597
#
2598
sub Error
2599
{
2600
    $logger->err("@_");
2601
}
2602
 
2603
sub Verbose
2604
{
1042 dpurdie 2605
    $logger->verbose2("@_");
1038 dpurdie 2606
}
2607
 
2608
sub Warning
2609
{
2610
    $logger->warn("@_");
2611
}
2612