Subversion Repositories DevTools

Rev

Rev 5300 | Rev 5399 | 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/;
25
 
26
use FindBin;                                    # Determine the current directory
27
use lib "$FindBin::Bin/lib";                    # Allow local libraries
28
 
29
use Utils;
30
use StdLogger;                                  # Log to sdtout
31
use Logger;                                     # Log to file
32
 
33
#
34
#   Database interface
35
#   Pinched from jats and modified so that this software is not dependent on JATS
36
#
37
use IO::Handle;
38
use JatsRmApi;
39
use DBI;
40
 
41
#
42
#   Globals
43
#
44
my $logger = StdLogger->new();                  # Stdout logger. Only during config
45
my $name = basename( $ARGV[0]);
46
   $name =~ s~.conf$~~;
47
my $now = 0;
5398 dpurdie 48
my $startTime = 0;
3515 dpurdie 49
my $tar = 'tar';
50
my $gzip = 'gzip';
1038 dpurdie 51
my $tagDirTime = 0;
52
my $lastDirScan = 0;
53
my $lastReleaseScan = 0;
1040 dpurdie 54
my $releaseScanMode = 0;
55
my $lastTagListScan = 0;
1038 dpurdie 56
my $transferred;
57
my $mtimeConfig = 0;
58
my $conf;
59
my $extraPkgs;
1040 dpurdie 60
my $excludePkgs;
61
my %releaseData;
3515 dpurdie 62
my $comError = 0;
5398 dpurdie 63
my $yday = -1;
1038 dpurdie 64
 
65
#
5398 dpurdie 66
#   Contain statisics maintained while operating
67
#       Can be dumped with a kill -USR2
68
#       List here for documentation
69
#  
70
 
71
my %statistics = (
72
    SeqNum => 0,                        # Bumped when $statistics are dumped
73
    timeStamp => 0,                     # DateTime when statistics are dumped
74
    upTime => 0,                        # Seconds since program start
75
    Cycle => 0,                         # Major process loop counter
76
    phase => 'Init',                    # Current phase of operation
77
    state => 'OK',                      # Nagios state
78
                                        # 
79
                                        # The following are reset each day
80
    dayStart => 0,                      # DateTime when daily data was reset
81
    txCount => 0,                       # Packages Transferred
82
    delCount => 0,                      # Packages marked for deletion
83
    staleTags => 0,                     # Stale Tags
84
    linkErrors => 0,                    # Transfer errors
85
                                        # 
86
                                        # Per Cycle Data - Calculated each processing Cycle
87
    total    => 0,                      # Packages on target machine
88
    delete   => 0,                      # Packages to delete
89
    excluded => 0,                      # Packages excluded    
90
    filtered => 0,                      # Packages filtered out
91
    missing  => 0,                      # Packages missing
92
    transfer => 0,                      # Packages to transfer
93
    writable => 0,                      # Packages still writable - thus not transferred
94
    tagCount => 0,                      # Packages tagged to be transferred
95
);
96
 
1038 dpurdie 97
#
5398 dpurdie 98
#   Describe configuration parameters
99
#
1038 dpurdie 100
my %cdata = (
101
    '.ignore'         => {'pkg\.(.+)' => 'pkgs' },
1048 dpurdie 102
    'piddir'          => {'mandatory' => 1      , 'fmt' => 'dir'},
103
    'sleep'           => {'default'   => 5      , 'fmt' => 'period'},
104
    'dpkg_archive'    => {'mandatory' => 1      , 'fmt' => 'dir'},
105
    'logfile'         => {'mandatory' => 1      , 'fmt' => 'vfile'},
106
    'logfile.size'    => {'default'   => '1M'   , 'fmt' => 'size'},
107
    'logfile.count'   => {'default'   => 9      , 'fmt' => 'int'},
108
    'verbose'         => {'default'   => 0      , 'fmt' => 'int'},
109
    'user'            => {'mandatory' => 1      , 'fmt' => 'text'},
110
    'hostname'        => {'mandatory' => 1      , 'fmt' => 'text'},
3847 dpurdie 111
    'sshport'         => {'default'   => 0      , 'fmt' => 'int'},
1048 dpurdie 112
    'identity'        => {'mandatory' => 1      , 'fmt' => 'file'},
113
    'bindir'          => {'mandatory' => 1      , 'fmt' => 'text'},
114
    'tagdir'          => {'mandatory' => 1      , 'fmt' => 'dir'},
115
    'forcedirscan'    => {'default'   => 100    , 'fmt' => 'period'},
116
    'tagage'          => {'default'   => '10m'  , 'fmt' => 'period'},
117
    'tagListUpdate'   => {'default'   => '1h'   , 'fmt' => 'period'},
118
    'synctime'        => {'default'   => '2h'   , 'fmt' => 'period'},
119
    'syncretry'       => {'default'   => '5m'   , 'fmt' => 'period'},
120
    'allProjects'     => {'default'   => 0      , 'fmt' => 'bool'},
4456 dpurdie 121
    'allArchive'      => {'default'   => 0      , 'fmt' => 'bool'},
1048 dpurdie 122
    'project'         => {'mandatory' => 0      , 'fmt' => 'int_list'},
123
    'release'         => {'mandatory' => 0      , 'fmt' => 'int_list'},
124
    'writewindow'     => {'default'   => '3h'   , 'fmt' => 'period'},
125
    'maxpackages'     => {'default'   => 5      , 'fmt' => 'int'},
126
    'deletePackages'  => {'default'   => 0      , 'fmt' => 'bool'},
127
    'deleteImmediate' => {'default'   => 0      , 'fmt' => 'bool'},
128
    'deleteAge'       => {'default'   => 0      , 'fmt' => 'period'},
129
    'packageFilter'   => {'default'   => undef  , 'fmt' => 'text'},
130
    'active'          => {'default'   => 1      , 'fmt' => 'bool'},
1038 dpurdie 131
);
132
 
133
 
134
#
135
#   Read in the configuration
136
#       Set up a logger
137
#       Write a pidfile - thats not used
138
readConfig();
139
Utils::writepid($conf);
140
$logger->logmsg("Starting...");
141
sighandlers($conf);
5398 dpurdie 142
$startTime = time();
1038 dpurdie 143
 
144
#
145
#   Main processing loop
146
#   Will exit when terminated by parent
147
#
3847 dpurdie 148
while (1 )
1038 dpurdie 149
{
150
    $logger->verbose3("Processing");
5398 dpurdie 151
    $statistics{Cycle}++;
1038 dpurdie 152
    $now = time();
1040 dpurdie 153
 
1038 dpurdie 154
    $transferred = {};
5398 dpurdie 155
    $statistics{phase} = 'ReadConfig';
1038 dpurdie 156
    readConfig();
1048 dpurdie 157
    if ( $conf->{'active'} )
158
    {
5398 dpurdie 159
        $statistics{phase} = 'ProcessReleaseList';
1048 dpurdie 160
        processReleaseList();
5398 dpurdie 161
        $statistics{phase} = 'processTags';
1048 dpurdie 162
        processTags();
5398 dpurdie 163
        $statistics{phase} = 'maintainTagList';
1048 dpurdie 164
        maintainTagList();
165
    }
1040 dpurdie 166
    %releaseData = ();
167
 
5398 dpurdie 168
    $statistics{phase} = 'Sleep';
1038 dpurdie 169
    sleep( $conf->{'sleep'} );
1050 dpurdie 170
 
171
    #
172
    #   Reap any and all dead children
173
    #
5398 dpurdie 174
    $statistics{phase} = 'Reaping';
1050 dpurdie 175
    my $kid;
176
    do {
177
        $kid = waitpid(-1, WNOHANG);
178
    } while ( $kid > 0 );
3847 dpurdie 179
 
180
    #   If my PID file ceases to be, then exit the daemon
181
    #   Used to force daemon to restart
182
    #
183
    unless ( -f $conf->{'pidfile'} )
184
    {
185
        $logger->logmsg("Terminate. Pid file removed");
186
        last;
187
    }
1038 dpurdie 188
}
5398 dpurdie 189
$statistics{phase} = 'Terminated';
1038 dpurdie 190
$logger->logmsg("Child End");
191
exit 0;
192
 
193
#-------------------------------------------------------------------------------
194
# Function        : readConfig
195
#
196
# Description     : Re read the config file if it modification time has changed
197
#
198
# Inputs          : Nothing
199
#
1289 dpurdie 200
# Returns         : 0       - Config not read
201
#                   1       - Config read
202
#                             Config file has changed
1038 dpurdie 203
#
204
sub readConfig
205
{
206
    my ($mtime) = Utils::mtime($ARGV[0]);
1289 dpurdie 207
    my $rv = 0;
208
 
1038 dpurdie 209
    if ( $mtimeConfig != $mtime )
210
    {
211
        $logger->logmsg("Reading config file: $ARGV[0]");
212
        $mtimeConfig = $mtime;
213
        my $errors;
214
        ($conf, $errors) = Utils::readconf ( $ARGV[0], \%cdata );
215
        if ( scalar @{$errors} > 0 )
216
        {
217
            warn "$_\n" foreach (@{$errors});
218
            die ("Config contained errors\n");
219
        }
220
 
221
        #
222
        #   Reset some information
223
        #   Create a new logger
224
        #
225
        $logger = Logger->new($conf);
226
        $conf->{logger} = $logger;
227
        $conf->{'pidfile'} = $conf->{'piddir'} . '/' . $name . '.pid';
228
        $logger->verbose("Log Levl: $conf->{verbose}");
229
 
230
        #
5398 dpurdie 231
        #   Setup statistics filename
232
        $conf->{'statsfile'} = $conf->{'piddir'} . '/' . $name . '.stats';
233
        $conf->{'statsfiletmp'} = $conf->{'piddir'} . '/' . $name . '.stats.tmp';
234
 
235
        #
1038 dpurdie 236
        #   Extract extra package config
237
        #
238
        $extraPkgs = {};
1040 dpurdie 239
        $excludePkgs = {};
1038 dpurdie 240
        while (my($key, $data) = each ( %{$conf->{pkgs}} ))
241
        {
3846 dpurdie 242
            if ( $data eq 'EXCLUDE' ) {
1040 dpurdie 243
                $excludePkgs->{$key} = 1;
244
                $logger->verbose("Exclude Pkg: $key");
3846 dpurdie 245
 
246
            } elsif ( $data eq 'ALL' ) {
247
                foreach my $pver (getPackageVersions($key))
248
                {
249
                    $extraPkgs->{$key}{$pver} = 1;
250
                    $logger->verbose("Extra Pkg: $key -> $pver");
251
                }
252
            } else {
1040 dpurdie 253
                $extraPkgs->{$key}{$data} = 1;
254
                $logger->verbose("Extra Pkg: $key -> $data");
255
            }
1038 dpurdie 256
        }
1046 dpurdie 257
 
1048 dpurdie 258
        $logger->verbose("Filter Packages: " . $conf->{'packageFilter'})
259
            if ( defined $conf->{'packageFilter'} );
260
 
3847 dpurdie 261
        $logger->warn("Non standard ssh port: " . $conf->{'sshport'})
262
            if ( $conf->{'sshport'} );
263
 
1048 dpurdie 264
        $logger->warn("Transfer session configured as not active")
265
            unless ( $conf->{'active'} );
266
 
267
        $logger->warn("Transfer all projects packages")
1050 dpurdie 268
            if ( $conf->{'allProjects'} );
4456 dpurdie 269
        $logger->warn("Transfer entire package archive")
270
            if ( $conf->{'allArchive'} );
1048 dpurdie 271
 
4456 dpurdie 272
 
1046 dpurdie 273
        #
274
        #   When config is read force some actions
275
        #       - Force tagList to be created
1050 dpurdie 276
        #       - Force release scan
1048 dpurdie 277
        $lastTagListScan = 0;
1050 dpurdie 278
        $lastReleaseScan = 0;
1289 dpurdie 279
        $rv = 1;
1038 dpurdie 280
    }
1289 dpurdie 281
    return $rv;
1038 dpurdie 282
}
283
 
284
 
285
#-------------------------------------------------------------------------------
286
# Function        : processReleaseList
287
#
288
# Description     : Process the release list
289
#                       Determine if its time to process release list
290
#                       Determine release list
291
#                       Determine release content
292
#                       Determine new items
293
#
294
# Inputs          : None
295
#
296
# Returns         : Nothing
297
#
298
sub processReleaseList
299
{
300
    #
1044 dpurdie 301
    #   Is Release List Processing active
302
    #   Can configure blat to disable release sync
303
    #   This will then allow 'new' packages to be sent
304
    #
305
    if ( $conf->{maxpackages} == 0 || $conf->{'synctime'} <= 0)
306
    {
307
        $logger->verbose2("processReleaseList disabled");
308
        return;
309
    }
310
 
311
    #
1038 dpurdie 312
    #   Time to perform the scan
313
    #   Will do at startup and every time period there after
314
    #
1040 dpurdie 315
    my $wtime = $releaseScanMode ? $conf->{'syncretry'} : $conf->{'synctime'};
316
    return unless ( $now > ($lastReleaseScan + $wtime ));
1038 dpurdie 317
    $logger->verbose("processReleaseList");
318
    $lastReleaseScan = $now;
1040 dpurdie 319
    $releaseScanMode = 1;                                   # Assume error
1038 dpurdie 320
 
321
    #
322
    #   Get list of packages from Remote site
323
    #   Invoke a program on the remote site and parse the results
324
    #
325
    #   Returned data looks like:
1042 dpurdie 326
    #   1141792602 GMT(Wed Mar  8 04:36:42 2006) [DL] ishieldmodules/11.5.0.cots
1038 dpurdie 327
    #   
328
    #
329
    my $remotePkgList;
330
    my $ph;
1040 dpurdie 331
    my $tgt_cmd = "$conf->{'bindir'}/get_plist.pl";
3515 dpurdie 332
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 333
 
334
    $logger->verbose2("processReleaseList:ssh_cmd:$ssh_cmd");
335
    open ($ph, "$ssh_cmd |");
336
    while ( <$ph> )
337
    {
338
        chomp;
1042 dpurdie 339
        if ( parsePkgList($_, \%{$remotePkgList} ) )
1038 dpurdie 340
        {
341
            $logger->verbose2("processReleaseList:Data: $_");
342
        }
343
        else
344
        {
345
            $logger->warn("processReleaseList:Bad Data: $_");
346
        }
347
    }
348
    close ($ph);
349
    $logger->verbose("processReleaseList:End: $?");
3515 dpurdie 350
    LogTxError ($?);
1038 dpurdie 351
    if ( $? != 0 )
352
    {
353
        $logger->warn("Cannot retrieve package list: $?");
5398 dpurdie 354
        $statistics{state} = 'No Remote Package List';
1038 dpurdie 355
        return;
356
    }
3515 dpurdie 357
 
1042 dpurdie 358
#Utils::DebugDumpData ("remotePkgList", $remotePkgList);
4456 dpurdie 359
 
1038 dpurdie 360
    #
361
    #   Determine the set of packages in the releases to be transferred
4456 dpurdie 362
    # 
363
    my $pkgList;
364
    if ( $conf->{'allArchive'} )
1040 dpurdie 365
    {
4456 dpurdie 366
        #   Examine entire archive
367
        #
368
        $pkgList = getArchiveList();
1040 dpurdie 369
    }
4456 dpurdie 370
    else
371
    {
372
        #   Examine Releases
373
        #
374
        my @rlist = getReleaseList();
375
        unless ( @rlist )
376
        {
377
            $logger->verbose2("No Releases to Process");
5398 dpurdie 378
            $statistics{state} = 'No Releases found';
4456 dpurdie 379
            return;
380
        }
381
        $pkgList = getPkgList(@rlist);
382
    }
1038 dpurdie 383
 
384
    #
385
    #   Append extra packages
386
    #   These are packages that are specifically named by the user
387
    #
388
    #   Note: If there are symbolic links, then the target of the
389
    #         link is treated used as the package name
390
    #
391
    #         Symlink MUST be within the same directory
3846 dpurdie 392
    #           Used to transfer jats2_current
1038 dpurdie 393
    #
394
    my $pkgLink;
395
    while ( (my ($pname, $pvers)) = each %{$extraPkgs} ) {
396
        while ( (my ($pver, $pdata) ) = each %{$pvers} ) {
397
 
1040 dpurdie 398
            my $epath = catfile( $conf->{'dpkg_archive'} , $pname, $pver );
1038 dpurdie 399
            if ( -l $epath )
400
            {
401
                my $lver = readlink( $epath );
402
                if ( ! defined $lver )
403
                {
3846 dpurdie 404
                    $logger->warn("Can't resolve symlink: $pname, $pver");
1038 dpurdie 405
                    next;
406
                }
407
 
408
                if ( $lver =~ m ~/~ )
409
                {
410
                    $logger->warn("Won't resolve symlink: $pname, $pver, $lver");
411
                    next;
412
                }
413
                $pkgLink->{$pname}{$pver} = $lver;
414
                $pdata = $pver;
415
                $pver = $lver;
416
            }
417
 
418
            $logger->verbose2("Add extra package: $pname, $pver, $pdata");
419
            $pkgList->{$pname}{$pver} = $pdata;
420
        }
421
    }
422
 
1040 dpurdie 423
    #
424
    #   If there are no packages to process, then assume that this is an error
425
    #   condition. Retry the operation soon.
426
    #
427
    unless ( keys %{$pkgList} )
428
    {
429
 
430
        $logger->verbose2("No packages to process");
5398 dpurdie 431
        $statistics{state} = 'No Packages found';
1040 dpurdie 432
        return;
433
    }
434
 
1038 dpurdie 435
#    while ( (my ($pname, $pvers)) = each %{$pkgList} )
436
#    {
437
#        while ( (my ($pver, $ptime) ) = each %{$pvers} )
438
#        {
439
#            print "L-- $pname, $pver, $ptime \n";
440
#
441
#        }
442
#    }
443
 
444
    #
1040 dpurdie 445
    #   Delete Excess Packages
1038 dpurdie 446
    #       Packages not required on the target
1042 dpurdie 447
    #           KLUDGE: Don't delete links to packages
448
    #           Don't delete packages marked for deletion
1038 dpurdie 449
    #
450
    my $excessPkgList;
1048 dpurdie 451
    my $excessPkgListCount = 0;
1038 dpurdie 452
    if ( $conf->{deletePackages} )
453
    {
454
        while ( (my ($pname, $pvers)) = each %{$remotePkgList} )
455
        {
456
            while ( (my ($pver, $pdata) ) = each %{$pvers} )
457
            {
458
                if ( !exists $pkgList->{$pname}{$pver} )
459
                {
460
                    if ( exists $pkgLink->{$pname}{$pver} )
461
                    {
462
                        $logger->verbose2("Keep Excess package-link: ${pname}/${pver}");
463
                        next;
464
                    }
1040 dpurdie 465
 
466
                    if ( exists $excludePkgs->{$pname} )
467
                    {
468
                        $logger->verbose2("Keep Excluded package: ${pname}");
469
                        next;
470
                    }
471
 
1042 dpurdie 472
                    if ( exists $pdata->{deleted} )
473
                    {
474
                        if ( $conf->{deleteAge} )
475
                        {
476
                            if ( $pdata->{deleted} <= $conf->{deleteAge} )
477
                            {
478
                                $logger->verbose2("Already marked for future age deletion: ${pname}/${pver}, $pdata->{deleted}");
479
                                next;
480
                            }
481
                            $pdata->{FORCEDELETE} = 1;
482
                        }
483
 
484
                        if ( !$conf->{deleteImmediate} )
485
                        {
486
                            $logger->verbose2("Already marked for deletion: ${pname}/${pver}");
487
                            next;
488
                        }
489
                    }
490
 
491
                    #
492
                    #   Force deletion
493
                    #       deleteImmediate mode
494
                    #       target is a broken link
495
                    #
496
                    $pdata->{FORCEDELETE} = 1
497
                        if ($conf->{deleteImmediate} || $pdata->{broken});
498
 
1038 dpurdie 499
                    $excessPkgList->{$pname}{$pver} = $pdata;
1048 dpurdie 500
                    $excessPkgListCount++;
1038 dpurdie 501
                    $logger->verbose("Excess package: ${pname}/${pver}");
502
                }
1050 dpurdie 503
#                else
504
#                {
505
#                        $logger->verbose3("Retain package: ${pname}/${pver}");
506
#                }
1038 dpurdie 507
            }
508
        }
509
    }
510
 
511
    #
512
    #   Process the remote list and the local list
513
    #   The remote time-stamp is the modification time of the packages descpkg file
514
    #
515
    #   Mark for transfer packages that
1040 dpurdie 516
    #       Are in the local set but not the remote set
1038 dpurdie 517
    #       Have a different time stamp
518
    #
519
    #   Ignore packages not in the local archive
520
    #   Ignore packages that don't have a descpkg
521
    #   Ignore packages that are writable - still being formed
522
    #
523
    my $needPkgList;
1048 dpurdie 524
    my $needPkgListCount = 0;
525
    my $filteredCount = 0;
526
    my $missingCount = 0;
527
    my $writableCount = 0;
528
    my $excludeCount = 0;
1038 dpurdie 529
    while ( (my ($pname, $pvers)) = each %{$pkgList} )
530
    {
1040 dpurdie 531
        #
532
        #   Ignore excluded packages
533
        #
1048 dpurdie 534
        if ( exists $excludePkgs->{$pname} )
535
        {
536
            $excludeCount++;
537
            next;
538
        }
1040 dpurdie 539
 
1048 dpurdie 540
        #
541
        #   Ignore packages that are filtered out
542
        #
543
        if ( defined $conf->{'packageFilter'} )
544
        {
545
            unless ( $pname =~ m~$conf->{'packageFilter'}~ )
546
            {
547
                $logger->verbose3("Filtering out: ${pname}");
548
                $filteredCount++;
549
                next;
550
            }
551
        }
552
 
1038 dpurdie 553
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
554
        {
1042 dpurdie 555
            my $tmtime = $remotePkgList->{$pname}{$pver}{time} || 0;
1038 dpurdie 556
 
557
            # Package is present in both list
1040 dpurdie 558
            my ($mtime, $mode) = Utils::mtime( catfile( $conf->{'dpkg_archive'} , $pname, $pver, 'descpkg' ));
1038 dpurdie 559
            if ( $mtime == 0 )
560
            {
561
                # PackageVersion not in local archive (at least the descpkg file is not)
562
                # Skip now - will pick it up later
563
                $logger->verbose("Package not in dpkg_archive: $pname, $pver");
1048 dpurdie 564
                $missingCount++;
1038 dpurdie 565
                next;
566
            }
567
 
568
            if ( $mode & 0222 )
569
            {
570
                # Descpkg file is writable
571
                # Package may be in the process of being created
1048 dpurdie 572
                # If the package has been writable for a long time, then
1038 dpurdie 573
                # consider for transfer
574
                my $age = $now - $mtime;
575
                if ( $age < ($conf->{'writewindow '} || 600) )
576
                {
577
                    $logger->verbose("Package is writable: $pname, $pver, ", $now - $mtime);
1048 dpurdie 578
                    $writableCount++;
1038 dpurdie 579
                    next;
580
                }
581
            }
582
 
583
            if ( $mtime != $tmtime )
584
            {
585
                # Package not present on target, or timestamps differ
586
                $logger->verbose("Package Needs to be transferred: $pname, $pver, $mtime, $tmtime");
587
                $needPkgList->{$pname}{$pver} = $pdata;
1048 dpurdie 588
                $needPkgListCount++;
1038 dpurdie 589
                next;
590
            }
591
        }
592
    }
593
 
1048 dpurdie 594
 
1038 dpurdie 595
    #
596
    #   Debug output only
597
    #   Display what we need to transfer
598
    #
599
    if ( $conf->{verbose} > 2 )
600
    {
601
        while ( (my ($pname, $pvers)) = each %{$needPkgList} )
602
        {
603
            while ( (my ($pver, $pdata) ) = each %{$pvers} )
604
            {
605
                $logger->verbose("Need to transfer: $pname, $pver, $pdata");
606
            }
607
        }
608
    }
1048 dpurdie 609
    if ( $conf->{verbose}  )
610
    {
611
        $logger->verbose("Packages to transfer: $needPkgListCount");
612
        $logger->verbose("Packages to delete: $excessPkgListCount");
613
        $logger->verbose("Packages filtered out: $filteredCount");
614
        $logger->verbose("Packages missing: $missingCount");
615
        $logger->verbose("Packages still writable: $writableCount");
616
        $logger->verbose("Packages excluded: $excludeCount");
617
    }
1038 dpurdie 618
 
619
    #
5398 dpurdie 620
    #   Update stats
621
    #   At this point we are looking pretty good
622
    #   
623
    $statistics{state} = 'OK';
624
    $statistics{total} = scalar keys(%{$remotePkgList});
625
    $statistics{transfer} = $needPkgListCount;
626
    $statistics{delete} = $excessPkgListCount;
627
    $statistics{filtered} = $filteredCount;
628
    $statistics{missing} = $missingCount;
629
    $statistics{writable} = $writableCount;
630
    $statistics{excluded} = $excludeCount;
631
 
632
    #
1038 dpurdie 633
    #   Time to do the real work
634
    #   Transfer packages and delete excess packages
635
    #   Note: Perform the transfers first
636
    #         Limit the number of packages processed in one pass
637
    #
638
    my $txcount = $conf->{maxpackages};
639
 
640
    #
641
    #   Transfer packages that we have identified
642
    #
643
    send_pkgs:
644
    while ( (my ($pname, $pvers)) = each %{$needPkgList} )
645
    {
646
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
647
        {
648
            if ( --$txcount <= 0 )
649
            {
1050 dpurdie 650
                $logger->warn("Max transfer count exceeded: $needPkgListCount transfer remaining");
1038 dpurdie 651
                $lastReleaseScan = 0;
652
                last send_pkgs;
653
            }
1289 dpurdie 654
 
655
            if ( readConfig() )
656
            {
657
                $logger->warn("Config file changed");
658
                $lastReleaseScan = 0;
659
                $txcount = 0;
660
                last send_pkgs;
661
            }
662
 
1044 dpurdie 663
            transferPackage ($pname, $pver, $pdata);
1048 dpurdie 664
            $needPkgListCount--;
1038 dpurdie 665
        }
666
    }
667
 
668
    #
669
    #   Delete packages that have been identified as excess
670
    #
671
    delete_pkgs:
672
    while ( (my ($pname, $pvers)) = each %{$excessPkgList} )
673
    {
674
        while ( (my ($pver, $pdata) ) = each %{$pvers} )
675
        {
676
            if ( --$txcount <= 0 )
677
            {
1050 dpurdie 678
                $logger->warn("Max transfer count exceeded: $excessPkgListCount deletion remaining");
1038 dpurdie 679
                $lastReleaseScan = 0;
680
                last delete_pkgs;
681
            }
1289 dpurdie 682
 
683
            if ( readConfig() )
684
            {
685
                $logger->warn("Config file changed");
686
                $lastReleaseScan = 0;
687
                $txcount = 0;
688
                last send_pkgs;
689
            }
690
 
1044 dpurdie 691
            deletePackage ($pname, $pver, $pdata);
1048 dpurdie 692
            $excessPkgListCount--;
1038 dpurdie 693
        }
694
    }
695
 
696
    #
697
    #   Send package list to the target
698
    #
699
    sendPackageList ($pkgList);
1040 dpurdie 700
 
701
    #
702
    #   On a successful transfer
703
    #       Force tag processing
704
    #       Set scan Mode to normal
705
    #
706
    $tagDirTime = 0;
707
    $releaseScanMode = 0;
1038 dpurdie 708
}
709
 
710
#-------------------------------------------------------------------------------
711
# Function        : sendPackageList
712
#
713
# Description     : Transfer package list to the target
714
#
715
# Inputs          : $pkgList            - Ref to hash of package names and versions
716
#
1040 dpurdie 717
# Returns         : Nothing
718
#                   Don't really care about any errors from this process
719
#                   Its not essential
1038 dpurdie 720
#
721
sub sendPackageList
722
{
723
    my ($pkgList) = @_;
724
    my ($fh, $filename) = tempfile( "/tmp/blat.$$.XXXX", SUFFIX => '.txt');
725
    $logger->verbose("sendPackageList:TmpFile: $filename");
726
 
727
    #
728
    #   Create a temp file with data
729
    #
730
    foreach my $pname ( sort keys %{$pkgList} )
731
    {
732
        foreach my $pver ( sort keys %{$pkgList->{$pname}} )
733
        {
734
            print $fh "$pname/$pver\n";
735
        }
736
    }
737
    close $fh;
738
 
739
    #
740
    #   Transfer to target
741
    #   Create the process pipe to transfer the file
742
    #   gzip the file and pipe the result through a ssh session to the target machine
3515 dpurdie 743
    #   gzip -c filename |  ssh  ... "./receive_file filename"
1038 dpurdie 744
    #
745
    my $ph;
3515 dpurdie 746
    my $gzip_cmd = "$gzip --no-name -c \"$filename\"";
1040 dpurdie 747
    my $tgt_cmd = "$conf->{'bindir'}/receive_file \"ArchiveList\"";
3515 dpurdie 748
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 749
 
750
    $logger->verbose2("sendPackageList:gzip_cmd:$gzip_cmd");
751
    $logger->verbose2("sendPackageList:tgt_cmd:$tgt_cmd");
752
    $logger->verbose2("sendPackageList:ssh_cmd:$ssh_cmd");
753
 
754
    open ($ph, "$gzip_cmd | $ssh_cmd |");
755
    while ( <$ph> )
756
    {
757
        chomp;
758
        $logger->verbose2("sendPackageList:Data: $_");
759
    }
760
    close ($ph);
3515 dpurdie 761
    unlink $filename;
1038 dpurdie 762
    $logger->verbose("sendPackageList:End: $?");
3515 dpurdie 763
    LogTxError ($?);
1038 dpurdie 764
}
765
 
766
 
767
#-------------------------------------------------------------------------------
768
# Function        : getPkgList
769
#
770
# Description     : Determine a set of package versions within the list
771
#                   of provided releases
772
#
773
# Inputs          : @rlist              - A list of releases to examine
774
#
775
# Returns         : Ref to a hask of package versions
776
#
777
sub getPkgList
778
{
779
    my %pdata;
780
    my $RM_DB;
781
    connectRM(\$RM_DB);
782
    $logger->verbose("getPkgList");
783
 
784
    #
785
    #   Determine the releases that are in this project
786
    #   Build up an sql query
787
    #
788
    my @m_rlist;
789
    push @m_rlist,"rc.RTAG_ID=$_" foreach ( @_ );
790
    my $m_rlist = join ' OR ', @m_rlist;
1048 dpurdie 791
    my $m_sqlstr = "SELECT DISTINCT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION, pv.IS_DEPLOYABLE" .
1038 dpurdie 792
                    " FROM RELEASE_MANAGER.RELEASE_CONTENT rc, RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg" .
793
                    " WHERE ( $m_rlist ) AND rc.PV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID" .
794
                    " ORDER by PKG_NAME DESC";
795
    $logger->verbose3("getPkgList:Sql:$m_sqlstr");
796
 
797
    my $sth = $RM_DB->prepare($m_sqlstr);
798
    if ( defined($sth) )
799
    {
800
        if ( $sth->execute( ) )
801
        {
802
            if ( $sth->rows )
803
            {
804
                while (my @row = $sth->fetchrow_array )
805
                {
806
                    $logger->verbose2("getPkgList:Data:@row");
807
                    $pdata{$row[1]}{$row[2]} = 1;
808
                }
809
            }
810
            $sth->finish();
811
        }
812
    }
813
    else
814
    {
815
        $logger->warn("getPkgList: SQL Prepare failure");
816
    }
1050 dpurdie 817
 
818
   disconnectRM(\$RM_DB);
1038 dpurdie 819
   return \%pdata;
820
}
821
 
822
 
823
#-------------------------------------------------------------------------------
824
# Function        : getReleaseList
825
#
826
# Description     : Determine the list of releases to be proccessed
1040 dpurdie 827
#                   From:
828
#                       Convert projects to a list of releases
829
#                       Configured list of releases
1038 dpurdie 830
#
831
# Inputs          : None
832
#
833
# Returns         : A list of releases to be processed
834
#
835
sub getReleaseList
836
{
837
    my $RM_DB;
838
    my %rlist;
1048 dpurdie 839
    my $m_sqlstr;
1038 dpurdie 840
    $logger->verbose("getReleaseList");
841
 
842
    #
1040 dpurdie 843
    #   Cache data
844
    #   Only for one cycle of the main loop
845
    #
846
    if ( exists $releaseData{getReleaseList} )
847
    {
848
        $logger->verbose3("getReleaseList:Cache hit");
849
        return @{$releaseData{getReleaseList}};
850
    }
851
 
852
    #
1048 dpurdie 853
    #   All projects
1038 dpurdie 854
    #
1048 dpurdie 855
    if ( $conf->{'allProjects'} )
1038 dpurdie 856
    {
1048 dpurdie 857
        $m_sqlstr = "SELECT rt.RTAG_ID" .
858
                    " FROM RELEASE_MANAGER.RELEASE_TAGS rt" .
1050 dpurdie 859
                    " WHERE rt.OFFICIAL != 'A'";
860
                    #" AND rt.OFFICIAL != 'Y'";
1048 dpurdie 861
    }
862
    else
863
    {
1038 dpurdie 864
        #
1048 dpurdie 865
        #   Convert list of projects into a list of releases
1038 dpurdie 866
        #
1048 dpurdie 867
        my @plist = split /[,\s]+/, $conf->{'project'} || '';
868
        if ( @plist )
869
        {
870
            #
871
            #   Determine the releases that are in this project
872
            #   Build up an sql query
873
            #
874
            my @m_plist;
875
            push @m_plist,"PROJ_ID=$_" foreach ( @plist );
876
            my $m_plist = join ' OR ', @m_plist;
877
            $m_sqlstr = "SELECT rt.RTAG_ID" .
878
                        " FROM RELEASE_MANAGER.RELEASE_TAGS rt" .
5300 dpurdie 879
                        " WHERE ( $m_plist ) AND rt.OFFICIAL != 'A'";
880
                        #" AND rt.OFFICIAL != 'Y'";
1048 dpurdie 881
        }
882
    }
1038 dpurdie 883
 
1048 dpurdie 884
    if ( defined $m_sqlstr )
885
    {
1038 dpurdie 886
        $logger->verbose3("getReleaseList:Sql:$m_sqlstr");
1048 dpurdie 887
        connectRM(\$RM_DB);
1038 dpurdie 888
        my $sth = $RM_DB->prepare($m_sqlstr);
889
        if ( defined($sth) )
890
        {
891
            if ( $sth->execute( ) )
892
            {
893
                if ( $sth->rows )
894
                {
895
                    while (my @row = $sth->fetchrow_array )
896
                    {
897
                        $logger->verbose2("getReleaseList:Data:@row");
898
                        $rlist{$row[0]} = 1;
899
                    }
900
                }
901
                $sth->finish();
902
            }
903
        }
904
        else
905
        {
906
            $logger->warn("getReleaseList: SQL Prepare failure");
907
        }
1050 dpurdie 908
        disconnectRM(\$RM_DB);
1038 dpurdie 909
    }
910
 
911
    #
912
    #   Add in the user specified list of releases
913
    #
914
    my @rlist = split /[,\s]+/, $conf->{'release'} || '';
915
    $rlist{$_} = 1 foreach(@rlist);
916
 
917
    #
918
    #   Sort for pretty display only
919
    #
1040 dpurdie 920
    @{$releaseData{getReleaseList}} = sort {$a <=> $b} keys %rlist;
921
 
922
    return @{$releaseData{getReleaseList}};
1038 dpurdie 923
}
924
 
1040 dpurdie 925
#-------------------------------------------------------------------------------
3846 dpurdie 926
# Function        : getPackageVersions
927
#
928
# Description     : Get the list of package-versions available in the package
929
#                   store.
930
#
931
# Inputs          : pkgName             - The package name
932
#
933
# Returns         : Array of versions
934
#
935
sub getPackageVersions
936
{
937
    my ($pkgName) = @_;
938
    my @versionList;
939
 
940
    my $pkgDir = catfile($conf->{'dpkg_archive'} , $pkgName );
941
    my $dh;
942
 
943
    unless (opendir($dh, $pkgDir))
944
    {
945
        $logger->warn ("Can't opendir $pkgDir: $!");
946
        return @versionList;
947
    }
948
 
949
    #
950
    #   Process each entry
951
    #   Ignore those that start with a .
952
    #
953
    while (my $version = readdir($dh) )
954
    {
955
        next if ( $version =~ m~^\.~ );
956
        my $file = catfile($pkgDir, $version);
957
 
958
        next unless ( -d $file );
959
 
960
        push @versionList, $version;
961
        $logger->verbose3("getPackageVersions: $pkgName, $version");
962
    }
963
    closedir $dh;
964
    return @versionList;
965
}
966
 
967
#-------------------------------------------------------------------------------
4456 dpurdie 968
# Function        : getArchiveList 
969
#
970
# Description     : Get the entire set of package versions in the archive
971
#
972
# Inputs          : 
973
#
974
# Returns         : Ref to a hask of package versions
975
#
976
sub getArchiveList
977
{
978
    my $pkgDir = $conf->{'dpkg_archive'};
979
    my %archiveList;
980
    my $dh;
981
    my @pkgList;
982
 
983
    unless (opendir($dh, $pkgDir))
984
    {
985
        $logger->warn ("Can't opendir $pkgDir: $!");
986
        return \%archiveList;
987
    }
988
 
989
    #
990
    #   Process each entry
991
    #   Ignore those that start with a .
992
    #   Ignore files
993
    #
994
    while (my $pkgName = readdir($dh) )
995
    {
996
        next if ( $pkgName =~ m~^\.~ );
997
        my $file = catfile($pkgDir, $pkgName);
998
 
999
        next unless ( -d $file );
1000
        $logger->verbose3("getArchiveList: $pkgName");
1001
        push @pkgList, $pkgName;
1002
    }
1003
    closedir $dh;
1004
 
1005
    #   Now get the package versions
1006
    #       Sort for pretty display
1007
    foreach my $pname (sort @pkgList)
1008
    {
1009
        foreach my $pver (getPackageVersions($pname))
1010
        {
1011
            $archiveList{$pname}{$pver} = 1;
1012
        }
1013
    }
1014
 
1015
    return \%archiveList;
1016
}
1017
 
1018
#-------------------------------------------------------------------------------
1040 dpurdie 1019
# Function        : maintainTagList
1020
#
1021
# Description     : Maintain a data structure for the maintenance of the
1022
#                   tags directory
1023
#
1024
# Inputs          : None
1025
#
1026
# Returns         : Nothing
1027
#
1028
sub maintainTagList
1029
{
1030
    #
1031
    #   Time to perform the scan
1032
    #   Will do at startup and every time period there after
1033
    #
1034
    return unless ( $now > ($lastTagListScan + $conf->{tagListUpdate} ));
1035
    $logger->verbose("maintainTagList");
1036
    $lastTagListScan = $now;
1038 dpurdie 1037
 
1040 dpurdie 1038
    #
1039
    #   Get list of things
1040
    #
1041
    my %config;
4457 dpurdie 1042
    if ($conf->{'allArchive'} )
1043
    {
1044
        $config{allArchive} = 1
1045
    }
1046
    elsif ($conf->{'allProjects'} )
1047
    {
1048
        $config{allProjects} = 1;
1049
    }
1050
    else
1051
    {
1052
        %{$config{projects}} = map { $_ => 1 } split (/[,\s]+/, $conf->{'project'} || '');
1053
        %{$config{releases}} = map { $_ => 1 } getReleaseList();
1054
    }
1040 dpurdie 1055
 
1056
    #
1057
    #   Save data
1058
    #
1059
    my $dump =  Data::Dumper->new([\%config], [qw(*config)]);
1060
#print $dump->Dump;
1061
#$dump->Reset;
1062
 
1063
    #
1064
    #   Save config data
1065
    #
1066
    my $conf_file = catfile( $conf->{'tagdir'},'.config' );
1067
    $logger->verbose3("maintainTagList: Writting $conf_file");
1068
 
1069
    my $fh;
1070
    open ( $fh, '>', $conf_file ) or $logger->err("Can't create $conf_file: $!");
1071
    print $fh $dump->Dump;
1072
    close $fh;
1073
}
1074
 
1075
 
1038 dpurdie 1076
#-------------------------------------------------------------------------------
1077
# Function        : processTags
1078
#
1079
# Description     : Process tags and send marked package versions to the target
1080
#                       Determine if new tags are present
1081
#                       Process each tag
1082
#
1083
# Inputs          : None
1084
#
1085
# Returns         : Nothing
1086
#
1087
sub processTags
1088
{
1089
    #
1090
    #   Determine if new tags are present by examining the time
1091
    #   that the directory was last modified.
1092
    #
1093
    #   Allow for a forced scan to catch packages that did not transfer
1094
    #   on the first attempt
1095
    #
5398 dpurdie 1096
    my $tagCount = 0;
1038 dpurdie 1097
    my ($mtime) = Utils::mtime($conf->{'tagdir'} );
1098
    if ( ($mtime > $tagDirTime) || ($now > ($lastDirScan + $conf->{'forcedirscan'})) )
1099
    {
1042 dpurdie 1100
        $logger->verbose2("processTags: ",$conf->{'tagdir'}, $mtime - $tagDirTime, $now - $lastDirScan);
1038 dpurdie 1101
        $tagDirTime = $mtime;
1102
        $lastDirScan = $now;
1103
 
1104
        my $dh;
1105
        unless (opendir($dh, $conf->{'tagdir'}))
1106
        {
1107
            $logger->warn ("can't opendir $conf->{'tagdir'}: $!");
1108
            return;
1109
        }
1110
 
1111
        #
1112
        #   Process each entry
1113
        #   Ignore those that start with a .
1114
        #
1115
        while (my $tag = readdir($dh) )
1116
        {
1117
            next if ( $tag =~ m~^\.~ );
1118
            my $file = "$conf->{'tagdir'}/$tag";
1042 dpurdie 1119
            $logger->verbose3("processTags: $file");
1120
 
1038 dpurdie 1121
            next unless ( -f $file );
1122
            next if ( $tag  eq 'ReleaseList' );
1123
 
1124
            if ( $tag =~ m~(.+)::(.+)~  )
1125
            {
1126
                my $package = $1;
1127
                my $version = $2;
5398 dpurdie 1128
                $tagCount++;
1038 dpurdie 1129
                if ( transferPackage( $package, $version ))
1130
                {
1131
                    unlink $file;
1132
                }
1133
                else
1134
                {
1135
                    my ($mtime) = Utils::mtime( $file );
1136
                    if ( $now - $mtime > $conf->{'tagage'} )
1137
                    {
1138
                        $logger->warn ("Delete unsatisfied tag: $tag");
1139
                        unlink $file;
5398 dpurdie 1140
                        $statistics{staleTags}++;
1038 dpurdie 1141
                    }
1142
                }
1143
            }
1144
        }
1145
        closedir $dh;
1146
    }
5398 dpurdie 1147
    $statistics{tagCount} = $tagCount;
1038 dpurdie 1148
}
1149
 
1150
#-------------------------------------------------------------------------------
1151
# Function        : transferPackage
1152
#
1153
# Description     : Transfer specified package to target system
1154
#
1155
# Inputs          : $pname          - Name of the package
1156
#                   $pver           - Package version
1157
#                   $plink          - (optional) Symlink in same package
1158
#
1159
# Returns         : true    - Package transferred
1160
#                   false   - Package not transferred
1161
#
1162
sub transferPackage
1163
{
1164
    my ($pname, $pver, $plink ) = @_;
1165
    my $rv = 0;
1166
    $logger->logmsg("transferPackage: @_");
1167
 
1168
    #
1040 dpurdie 1169
    #   Do not transfer excluded files
1170
    #
1171
    if ( exists $excludePkgs->{$pname} )
1172
    {
1048 dpurdie 1173
        $logger->warn("transferPackage: Excluded package not transferred: $pname, $pver");
1040 dpurdie 1174
        return 1;
1175
    }
1048 dpurdie 1176
 
1040 dpurdie 1177
    #
1048 dpurdie 1178
    #   Apply package filter
1179
    #
1180
    if ( defined $conf->{'packageFilter'} )
1181
    {
1182
        unless ( $pname =~ m~$conf->{'packageFilter'}~ )
1183
        {
1184
            $logger->warn("transferPackage: Filtered out package not transferred: $pname, $pver");
1185
            return 1;
1186
        }
1187
    }
1188
 
1189
    #
1038 dpurdie 1190
    #   plink of 1 is not a symlink
1191
    #
1192
    $plink = undef if ( defined($plink) && $plink eq '1' );
1193
 
1194
    #
1195
    #   If its been transferred in the current scan, then
1196
    #   indicate that all is well
1197
    #
1198
    if ( $transferred->{$pname}{$pver}  )
1199
    {
1200
        $logger->verbose("transferPackage: Already transferred");
1201
        return 1;
1202
    }
1203
 
1040 dpurdie 1204
    my $sfile = catfile( $conf->{'dpkg_archive'} , $pname, $pver );
1038 dpurdie 1205
    unless ( -d $sfile )
1206
    {
1207
        $logger->warn("transferPackage:Package not found: $pname, $pver");
1208
        return $rv;
1209
    }
1210
 
1211
    #
4456 dpurdie 1212
    #   Create the process pipe to transfer the package
1038 dpurdie 1213
    #   Tar the directory and pipe the result through a ssh session to
1214
    #   the target machine
3515 dpurdie 1215
    #   $tar -czf - -C "$dpkg/${pname}/${pver}" . |  ssh  ... "./receive_package pname pver"
1038 dpurdie 1216
    #
1217
    my $ph;
3515 dpurdie 1218
    my $tar_cmd = "$tar -czf - -C \"$sfile\" .";
1038 dpurdie 1219
    my $tgt_opts = defined($plink) ? "\"-L$plink\"" : '';
1040 dpurdie 1220
    my $tgt_cmd = "$conf->{'bindir'}/receive_package $tgt_opts \"$pname\" \"$pver\"";
3515 dpurdie 1221
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 1222
 
1223
    $logger->verbose2("transferPackage:tar_cmd:$tar_cmd");
1224
    $logger->verbose2("transferPackage:tgt_cmd:$tgt_cmd");
1225
    $logger->verbose2("transferPackage:ssh_cmd:$ssh_cmd");
1226
 
1227
    open ($ph, "$tar_cmd | $ssh_cmd |");
1228
    while ( <$ph> )
1229
    {
1230
        chomp;
1231
        $logger->verbose2("transferPackage:Data: $_");
1232
    }
1233
    close ($ph);
1234
    $logger->verbose("transferPackage:End: $?");
1235
    if ( $? == 0 )
1236
    {
1237
        #
1238
        #   Mark has having been transferred in the current cycle
1239
        #
1240
        $transferred->{$pname}{$pver} = 1;
1241
        $rv = 1;
5398 dpurdie 1242
        $statistics{txCount}++;
1038 dpurdie 1243
    }
1244
    else
1245
    {
1246
        $logger->warn("transferPackage:Transfer Error: $pname, $pver, $?");
1247
    }
3515 dpurdie 1248
    LogTxError ($?);
1038 dpurdie 1249
    return $rv;
1250
}
1251
 
1252
#-------------------------------------------------------------------------------
1253
# Function        : deletePackage
1254
#
1255
# Description     : Delete specified package to target system
1256
#
1257
# Inputs          : $pname          - Name of the package
1258
#                   $pver           - Package version
1042 dpurdie 1259
#                   $pdata          - Hash of extra data
1038 dpurdie 1260
#
1261
# Returns         : true    - Package transferred
1262
#                   false   - Package not transferred
1263
#
1264
sub deletePackage
1265
{
1042 dpurdie 1266
    my ($pname, $pver, $pdata ) = @_;
1038 dpurdie 1267
    my $rv = 0;
1042 dpurdie 1268
    $logger->logmsg("deletePackage: $pname, $pver");
1038 dpurdie 1269
 
1270
    #
1271
    #   Create the process pipe to delete the package
3515 dpurdie 1272
    #   ssh  ... "./delete_package ${rx_opts} \"$pname\" \"$pver\""
1038 dpurdie 1273
    #
1274
    my $ph;
1042 dpurdie 1275
    my $flags = $pdata->{FORCEDELETE}  ? '' : ' -T';
1276
    my $tgt_cmd = "$conf->{'bindir'}/delete_package $flags \"$pname\" \"$pver\"";
3515 dpurdie 1277
    my $ssh_cmd = sshCmd($tgt_cmd);
1038 dpurdie 1278
 
1040 dpurdie 1279
    $logger->verbose2("deletePackage:tgt_cmd:$tgt_cmd");
1280
    $logger->verbose2("deletePackage:ssh_cmd:$ssh_cmd");
1038 dpurdie 1281
 
1282
    open ($ph, "$ssh_cmd |");
1283
    while ( <$ph> )
1284
    {
1285
        chomp;
1286
        $logger->verbose2("deletePackage:Data: $_");
1287
    }
1288
    close ($ph);
1289
    $logger->verbose("deletePackage:End: $?");
1290
    if ( $? == 0 )
1291
    {
1292
        $rv = 1;
5398 dpurdie 1293
        $statistics{delCount}++;
1038 dpurdie 1294
    }
1295
    else
1296
    {
1297
        $logger->warn("deletePackage:Error: $pname, $pver, $?");
1298
    }
3515 dpurdie 1299
    LogTxError ($?);
1038 dpurdie 1300
    return $rv;
1301
}
1302
 
3515 dpurdie 1303
#-------------------------------------------------------------------------------
1304
# Function        : sshCmd
1305
#
1306
# Description     : Generate a ssh based command
1307
#
1308
# Inputs          : Target command
1309
#
1310
# Returns         : An shh command string
1311
#
1312
sub sshCmd
1313
{
1314
    my ($tgt_cmd) = @_;
3847 dpurdie 1315
    my $sshPort = '';
1316
    $sshPort = "-p $conf->{'sshport'}"
1317
        if ($conf->{'sshport'});
1318
 
1319
    return "ssh -o \"BatchMode yes\" -i $conf->{'identity'} ${sshPort} $conf->{'user'}\@$conf->{'hostname'} \"$tgt_cmd\" 2>&1";
3515 dpurdie 1320
}
1038 dpurdie 1321
 
3515 dpurdie 1322
 
1038 dpurdie 1323
#-------------------------------------------------------------------------------
1042 dpurdie 1324
# Function        : parsePkgList
1325
#
1326
# Description     : Parse one line from a pkgList
1327
#                   Lines are multiple item="data" items
1328
#
1329
# Inputs          : $line                   - Line of data
1330
#                   $hashp                  - Ref to hash to populate
1331
#
1332
# Returns         : A hash of data items
1333
#
1334
sub parsePkgList
1335
{
1336
    my ($line, $hashp) = @_;
1337
    my $rv;
1338
 
1339
    while ( $line =~ m~\s*(.+?)="(.+?)"~ )
1340
    {
1341
        $rv->{$1} = $2;
1342
        $line = $';
1343
    }
1344
#Utils::DebugDumpData ("parsePkgList", $rv);
1345
 
1346
    my $pname = $rv->{pname};
1347
    my $pver =  $rv->{pver};
1348
    return undef unless ( $pname && $pver );
1349
 
1350
    delete $rv->{pname};
1351
    delete $rv->{pver};
1352
    delete $rv->{GMT};
1353
 
1354
    $hashp->{$pname}{$pver} = $rv;
1355
    return $hashp;
1356
}
1357
 
5398 dpurdie 1358
#-------------------------------------------------------------------------------
1359
# Function        : resetDailyStatistics 
1360
#
1361
# Description     : Called periodically to reset the daily statistics
1362
#
1363
# Inputs          : $time       - Current time
1364
#
1365
# Returns         : 
1366
#
1367
sub resetDailyStatistics
1368
{
1369
    my ($time) = @_;
1042 dpurdie 1370
 
5398 dpurdie 1371
    #
1372
    #   Detect a new day
1373
    #
1374
    my $today = (localtime($time))[7];
1375
    if ($yday != $today)
1376
    {
1377
        $yday = $today;
1378
        $logger->logmsg('Resetting daily statistics' );
1379
 
1380
        $statistics{dayStart} = $time;
1381
        $statistics{txCount} = 0;
1382
        $statistics{delCount} = 0;
1383
        $statistics{staleTags} = 0;
1384
        $statistics{linkErrors} = 0;
1385
    }
1386
}
1387
 
1042 dpurdie 1388
#-------------------------------------------------------------------------------
5398 dpurdie 1389
# Function        : periodicStatistics 
1390
#
1391
# Description     : Called on a regular basis to write out statistics
1392
#                   Used to feed information into Nagios
1393
#                   
1394
#                   This function is called via an alarm and may be outside the normal
1395
#                   processing loop. Don't make assumptions on the value of $now
1396
#
1397
# Inputs          : 
1398
#
1399
# Returns         : 
1400
#
1401
sub periodicStatistics
1402
{
1403
    #
1404
    #   A few local stats
1405
    #
1406
    $statistics{SeqNum}++;
1407
    $statistics{timeStamp} = time();
1408
    $statistics{upTime} = $statistics{timeStamp} - $startTime;
1409
 
1410
    #   Reset daily accumulations - on first use each day
1411
    resetDailyStatistics($statistics{timeStamp});
1412
 
1413
    #
1414
    #   Write statistics to a file
1415
    #       Write to a tmp file, then rename.
1416
    #       Attempt to make the operation atomic - so that the file consumer
1417
    #       doesn't get a badly formed file.
1418
    #   
1419
    if ($conf->{'statsfiletmp'})
1420
    {
1421
        my $fh;
1422
        unless (open ($fh, '>', $conf->{'statsfiletmp'}))
1423
        {
1424
            $fh = undef;
1425
            $logger->warn("Cannot create temp stats file: $!");
1426
        }
1427
        else
1428
        {
1429
            foreach my $key ( keys %statistics)
1430
            {
1431
                print $fh $key . ':' . $statistics{$key} . "\n";
1432
                $logger->verbose2('Statistics:'. $key . ':' . $statistics{$key});
1433
            }
1434
            close $fh;
1435
 
1436
            # Rename temp to real file
1437
            rename  $conf->{'statsfiletmp'},  $conf->{'statsfile'} ;
1438
        }
1439
    }
1440
}
1441
 
1442
#-------------------------------------------------------------------------------
1038 dpurdie 1443
# Function        : sighandlers
1444
#
1445
# Description     : Install signal handlers
1446
#
1447
# Inputs          : $conf           - System config
1448
#
1449
# Returns         : Nothing
1450
#
1451
sub sighandlers
1452
{
5398 dpurdie 1453
    my $conf = shift;
1454
    my $logger = $conf->{logger};
1038 dpurdie 1455
 
5398 dpurdie 1456
    $SIG{TERM} = sub {
1457
        # On shutdown
1458
        $logger->logmsg('Received SIGTERM. Shutting down....' );
1459
        unlink $conf->{'pidfile'} if (-f $conf->{'pidfile'});
1460
        unlink $conf->{'statsfile'} if (-f $conf->{'statsfile'});
1461
        unlink $conf->{'statsfiletmp'} if (-f $conf->{'statsfiletmp'});
1462
        exit 0;
1463
    };
1038 dpurdie 1464
 
5398 dpurdie 1465
    $SIG{HUP} = sub {
1466
        # On logrotate
1467
        $logger->logmsg('Received SIGHUP.');
1468
        $logger->rotatelog();
1469
    };
1038 dpurdie 1470
 
5398 dpurdie 1471
    $SIG{USR1} = sub {
1472
        # On Force Archive Sync
1473
        $logger->logmsg('Received SIGUSR1.');
1046 dpurdie 1474
        $lastReleaseScan = 0;
1475
        $lastTagListScan = 0;
5398 dpurdie 1476
    };
1038 dpurdie 1477
 
5398 dpurdie 1478
    alarm 60;
1479
    $SIG{ALRM} = sub {
1480
        # On Dump Statistics
1481
        $logger->verbose2('Received SIGUSR2.');
1482
        periodicStatistics();
1483
        alarm 60;
1484
    };
1485
 
1038 dpurdie 1486
    $SIG{__WARN__} = sub { $logger->warn("@_") };
1487
    $SIG{__DIE__} = sub { $logger->err("@_") };
1488
}
1489
 
1490
#-------------------------------------------------------------------------------
3515 dpurdie 1491
# Function        : LogTxError
1492
#
1493
# Description     : Detect restoration of communication and log such
1494
#                   Don't log failures as the user will do that
1495
#
1496
# Inputs          : $state                  - 0 - All is well
1497
#                                           !0  - Error
1498
#
1499
# Returns         : Nothing
1500
#
1501
sub LogTxError
1502
{
1503
    my ($state) = $@;
1504
    if ( $state )
1505
    {
5398 dpurdie 1506
        $statistics{linkErrors}++ unless($comError);
3515 dpurdie 1507
        $comError++;
5398 dpurdie 1508
        $statistics{state} = 'No Communication';
3515 dpurdie 1509
    }
1510
    elsif ( $comError )
1511
    {
1512
        $logger->warn("Communication Restored");
1513
        $comError = 0;
5398 dpurdie 1514
        $statistics{state} = 'OK';
3515 dpurdie 1515
    }
1516
}
1517
 
1518
 
1519
#-------------------------------------------------------------------------------
1038 dpurdie 1520
# Function        : Error, Verbose, Warning
1521
#
1522
# Description     : Support for JatsRmApi
1523
#
1524
# Inputs          : Message
1525
#
1526
# Returns         : Nothing
1527
#
1528
sub Error
1529
{
1530
    $logger->err("@_");
1531
}
1532
 
1533
sub Verbose
1534
{
1042 dpurdie 1535
    $logger->verbose2("@_");
1038 dpurdie 1536
}
1537
 
1538
sub Warning
1539
{
1540
    $logger->warn("@_");
1541
}
1542
 
1543
 
1544
 
1545