Subversion Repositories DevTools

Rev

Rev 5710 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
392 dpurdie 1
########################################################################
6177 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
392 dpurdie 3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : Determine packages from an SBOM for escrow purposes
10
#                 For a given bom_id determine all used packages
11
#                 Create various bits of useful information
12
#                   Extract commands
13
#                   Build Order
14
#                   Dependency Info
15
#                   Bad Packages
16
#
17
#
18
#......................................................................#
19
 
20
require 5.006_001;
21
use strict;
22
use warnings;
23
use JatsEnv;
24
use JatsError;
25
use JatsSystem;
26
use JatsRmApi;
27
use DBI;
28
use Getopt::Long;
29
use Pod::Usage;                             # required for help support
30
use Storable qw (dclone);
31
 
32
 
33
#
34
#   Config Options
35
#
36
my $VERSION = "1.0.0";              # Update this
37
my $opt_help = 0;
38
my $opt_manual;
39
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
40
my $opt_sbom_id;
41
my $opt_rtag_id;
42
my $opt_test = 0;
43
my $opt_patch = 1;
44
my $opt_extract;
45
 
46
#
47
#   Data Base Interface
48
#
49
my $RM_DB;
50
my $DM_DB;
51
 
52
#
53
#   Global variables
54
#
55
my %os_id_list;                 # os_id in the SBOM
56
my %os_env_list;                # OS Environments
57
my %pv_id;                      # Packages in the SBOM
58
my %Package;                    # Per Package information
59
my %Release;                    # Release information
60
my %Release_pvid;               # Release info
61
my @StrayPackages;              # Non-top level packages
62
my @create_list;                # List of files created
63
my $fpref = "sbom";             # Sbom Prefix
64
our $GBE_RM_URL;
65
our $GBE_DM_URL;
66
 
67
#
68
#   Constants, that should be variable
69
#
70
my $rm_base = "/dependencies.asp?pv_id=";
71
my $dm_base = "/OsDefault.asp?bom_id=BOMID&os_id=";
72
 
73
#
74
#   Build types. Should be populated from a table
75
#
76
my %BM_ID = (
77
    1 => "Solaris",
78
    2 => "Win32",
79
    3 => "Linux",
80
    4 => "Generic",
81
);
82
 
83
my %BSA_ID = (
84
    1 => "Jats Debug",
85
    2 => "Jats Prod",
86
    3 => "Jats Debug+Prod",
87
    4 => "Ant Java 1.4",
88
    5 => "Ant Java 1.5",
89
    6 => "Ant Java 1.6",
90
);
91
 
92
#
93
#   Packages to be ignored
94
#
95
my %ignore;
96
my %patch;
97
 
98
 
99
#-------------------------------------------------------------------------------
100
# Function        : Main
101
#
102
# Description     : Main entry point
103
#                   Parse user options
104
#
105
# Inputs          :
106
#
107
# Returns         :
108
#
109
 
110
my $result = GetOptions (
111
                "help+"         => \$opt_help,              # flag, multiple use allowed
112
                "manual"        => \$opt_manual,            # flag
113
                "verbose+"      => \$opt_verbose,           # flag
114
                "sbomid=s"      => \$opt_sbom_id,           # string
115
                "bom_id=s"      => \$opt_sbom_id,           # string
116
                "rtagid=s"      => \$opt_rtag_id,           # string
117
                "rtag_id=s"     => \$opt_rtag_id,           # string
118
                "ignore=s",     => sub{my ($a,$i) = @_; $ignore{$i} = 0 },
119
                "test!"         => \$opt_test,              #[no]flag
120
                "patch!"        => \$opt_patch,             #[no]flag
121
                "extract=s"     => \$opt_extract,           # Name of file
122
                );
123
 
124
#
125
#   Process help and manual options
126
#
127
pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
128
pod2usage(-verbose => 1)  if ($opt_help == 2 );
129
pod2usage(-verbose => 2)  if ($opt_manual || ($opt_help > 2));
130
 
131
ErrorConfig( 'name'    => 'ESCROW',
132
             'verbose' => $opt_verbose );
133
 
134
#
135
#   Sanity test
136
#
137
unless ( $opt_rtag_id || $opt_sbom_id || $opt_extract)
138
{
139
    Error ("Need sbomid and/or rtagid, or -extract",
140
           "Example: -sbomid=13543, for NZS Phase-1",
141
           "Example: -sbomid=13543 -rtagid=xxxx, for NZS Phase-1, comapred against given release",
142
           "Example: -rtagid=2362, for Sydney R1/R2",
143
    )
144
}
145
 
146
#
147
#   The extract option is special
148
#   It places the progam in a different mode
149
#
150
if ( $opt_extract )
151
{
152
    Error ("Cannot mix -extract with sbomid or rtagid" )
153
        if ( $opt_rtag_id || $opt_sbom_id );
154
 
155
    extract_files();
156
    exit (0);
157
 
158
}
159
 
160
Warning ("No sbomid provided. Output based an a Release") unless ( $opt_sbom_id );
161
$dm_base =~ s~BOMID~$opt_sbom_id~ if ($opt_sbom_id);
162
$fpref = "release" unless ( $opt_sbom_id );
163
 
164
#
165
#   Import essential EnvVars
166
#
167
EnvImport('GBE_RM_URL');
168
EnvImport('GBE_DM_URL');
169
 
170
$rm_base = $GBE_RM_URL . $rm_base;
171
$dm_base = $::GBE_DM_URL . $dm_base;
172
 
173
if ( $opt_sbom_id )
174
{
175
    #
176
    #   Determines the OS_ID's for the bom
177
    #
178
    getOSIDforBOMID($opt_sbom_id);
179
 
180
    #
181
    #   Locate packages associated with the base install for each os
182
    #
183
    foreach my $base_env_id ( sort keys %os_env_list )
184
    {
185
        getPackagesforBaseInstall( $base_env_id );
186
    }
187
 
188
    #
189
    #   Determine all the top level packages in the BOM
190
    #
191
    foreach my $os_id ( sort keys %os_id_list )
192
    {
193
        getPackages_by_osid( $os_id );
194
    }
195
 
196
 
197
    #
198
    #   For each Top Level Package determine the dependent packages
199
    #
200
    foreach my $pv_id ( keys %pv_id )
201
    {
202
        getPkgDetailsByPV_ID( $pv_id);
203
    }
204
    LocateStrays();
205
 
206
    #
207
    #   Determine packages in a given Release
208
    #
209
    if ( $opt_rtag_id )
210
    {
211
        getPkgDetailsByRTAG_ID( $opt_rtag_id );
212
    }
213
}
214
else
215
{
216
    getPkgDetailsByRTAG_ID( $opt_rtag_id );
217
my $count = 0;
218
    foreach my $pv_id ( keys %Release_pvid )
219
    {
220
next if ( $opt_test && ++$count > 3 );
221
        getPkgDetailsByPV_ID( $pv_id);
222
    }
223
    LocateStrays();
224
}
225
 
226
 
227
#
228
#   Remove packages to be ignored
229
#
230
foreach my $pkg ( keys %ignore )
231
{
232
    delete $Package{$pkg};
233
}
234
 
235
##
236
##   Display a list of all packages found so far
237
##
238
#foreach my $name ( sort keys %Package )
239
#{
240
#    foreach my $ver ( sort keys %{$Package{$name}} )
241
#    {
242
#
243
#        my $label = $Package{$name}{$ver}{label} || '';
244
#        my $path = $Package{$name}{$ver}{path} || '';
245
#
246
#        printf ("%30s %15s %45s %s\n", $name, $ver, $label, $path );
247
#    }
248
#}
249
 
250
#
251
#   Generate output files
252
#       1) Jats extract commands
253
#       2) Error list
254
my $file;
255
$file = "${fpref}_extract.txt";
256
push @create_list, $file;
257
open (JE, ">$file" ) || Error ("Cannot create $file");
258
 
259
$file = "${fpref}_status.txt";
260
push @create_list, $file;
261
open (ST, ">$file" ) || Error("Cannot create $file");
262
print ST "Cannot build:\n";
263
 
264
foreach my $name ( sort keys %Package )
265
{
266
    foreach my $ver ( sort keys %{$Package{$name}} )
267
    {
268
 
269
        my $label = $Package{$name}{$ver}{label} || '';
270
        my $path = $Package{$name}{$ver}{path} || '';
271
        my $mtest = exists ($Package{$name}{$ver}{build} ) || '0';
272
        my @reason1;            # can't extract files
273
        my @reason2;            # Others
274
 
275
        push @reason1, 'No Label' unless ( $label );
276
        push @reason1, 'Bad Label, N/A' if ( $label =~ s~^N/A$~~i || $label  =~ s~^na$~~i );
277
 
278
        push @reason1, 'No Source Path' unless ( $path );
279
        push @reason1, 'Bad Path, N/A' if ( $path =~ m~^N/A$~i || $path  =~ m~^na$~i );
280
        push @reason1, 'Bad Path, dpkg' if ( $path =~ m~^/dpkg_archive~ || $path  =~ m~^dpkg_archive~ );
281
        push @reason1, 'Bad Path, http' if ( $path =~ m~^http:~i );
282
        push @reason1, 'Bad Path, Drive' if ( $path =~ m~^[A-Za-z]\:~ );
283
        push @reason1, 'Bad Path, UNC' if ( $path =~ m~^//~ );
284
        push @reason1, 'Bad Path, Relative' unless ( $path =~ m~^/~ );
285
 
286
 
287
        push @reason2, 'No Build System' unless ( exists ($Package{$name}{$ver}{build} ) );
288
 
289
        unless ( @reason1 )
290
        {
291
            my $vname = "$name $ver";
292
            $vname =~ s~ ~_~g;
293
            $vname =~ s~__~~g;
294
 
295
            print JE "jats extract -extractfiles -view=$vname -label=$label -path=$path -root=. -noprefix\n";
296
        }
297
 
298
        if ( @reason1 || @reason2 )
299
        {
300
            $Package{$name}{$ver}{bad_extract} = [@reason1, @reason2];
301
            printf ST "%40s %20s %50s (%s) %s\n", $name, $ver, $label, $mtest, $path ;
302
        }
303
    }
304
}
305
 
306
close (JE);
307
close (ST);
308
 
309
#
310
#   Generate Leaf Package Information
311
#
312
LeafTracking();
313
 
314
#
315
#   Generate build order info
316
#
317
BuildOrder();
318
 
319
#
320
#   Generate HTML depenedancy information and other useful stuff
321
#
322
GenerateHTML();
323
 
324
 
325
#
326
#   Display names of files created
327
#
328
foreach my $file ( sort @create_list )
329
{
330
    Message ("Created: $file");
331
}
332
exit;
333
 
334
 
335
#-------------------------------------------------------------------------------
336
# Function        : getOSIDforBOMID
337
#
338
# Description     : Get all the os_id's associated with a BOMID
339
#
340
# Inputs          : $bom_id             - BOM to process
341
#
342
# Returns         :
343
#
344
 
345
sub getOSIDforBOMID
346
{
347
    my ($bom_id) = @_;
348
    my $foundDetails = 0;
349
    my (@row);
350
 
351
    connectDM(\$DM_DB) unless ($DM_DB);
352
 
353
    my $m_sqlstr = "SELECT distinct os.OS_ID, os.OS_NAME, nn.NODE_NAME, obe.BASE_ENV_ID " .
354
                   " FROM OPERATING_SYSTEMS os, BOM_CONTENTS bc, NETWORK_NODES nn, OS_BASE_ENV obe" .
355
                   " WHERE bc.BOM_ID = $bom_id AND bc.NODE_ID = os.NODE_ID AND nn.NODE_ID = os.NODE_ID AND obe.OS_ID = os.OS_ID ";
356
 
357
    my $sth = $DM_DB->prepare($m_sqlstr);
358
    if ( defined($sth) )
359
    {
360
        if ( $sth->execute( ) )
361
        {
362
            if ( $sth->rows )
363
            {
364
                while ( @row = $sth->fetchrow_array )
365
                {
366
                    Verbose ("OS_ID: ".join (',',@row) );
367
                    $os_id_list{$row[0]}{os_name} = $row[1];
368
                    $os_id_list{$row[0]}{node_name} = $row[2];
369
 
370
                    $os_env_list{$row[3]}{needed} = 1;
371
                    $os_env_list{$row[3]}{os_id}{$row[0]} = 1;
372
                }
373
            }
374
            $sth->finish();
375
        }
376
    }
377
    else
378
    {
379
        Error("getOSIDforBOMID:Prepare failure" );
380
    }
381
}
382
 
383
#-------------------------------------------------------------------------------
384
# Function        : getPackagesforBaseInstall
385
#
386
# Description     : Get all the packages for a given base install
387
#
388
# Inputs          :
389
#
390
# Returns         :
391
#
392
 
393
sub getPackagesforBaseInstall
394
{
395
    my ($base_env_id) =@_;
396
    my $foundDetails = 0;
397
    my (@row);
398
 
399
    connectDM(\$DM_DB) unless ($DM_DB);
400
 
401
    # First get details from pv_id
402
 
403
    my $m_sqlstr = "SELECT DISTINCT bec.PROD_ID, pkg.pkg_name, pv.pkg_version, pkg.pkg_id, pv.pv_id" .
404
                " FROM PACKAGES pkg, PACKAGE_VERSIONS pv,PRODUCT_DETAILS pd, BASE_ENV_CONTENTS bec".
405
                " WHERE bec.BASE_ENV_ID = $base_env_id AND bec.PROD_ID (+)= pv.PV_ID AND pv.pkg_id = pkg.pkg_id";
406
 
407
    my $sth = $DM_DB->prepare($m_sqlstr);
408
    if ( defined($sth) )
409
    {
410
        if ( $sth->execute( ) )
411
        {
412
            if ( $sth->rows )
413
            {
414
                while ( @row = $sth->fetchrow_array )
415
                {
416
                    Verbose ("OS ENV Package($base_env_id}:" . join (',',@row) );
417
 
418
                    my $pv_id =     $row[0];
419
                    my $name =      $row[1]  || 'BadName';
420
                    my $ver =       $row[2]  || 'BadVer';
421
 
422
                    $pv_id{$pv_id}{pkg_name} =$name;
423
                    $pv_id{$pv_id}{pkg_ver} = $ver;
424
                    foreach my $os_id ( keys %{$os_env_list{$base_env_id}{os_id}} )
425
                    {
426
                        $pv_id{$pv_id}{os_id}{$os_id} = 2;
427
                    }
428
                }
429
            }
430
            $sth->finish();
431
        }
432
        else
433
        {
434
            Error ("getPackagesforBaseInstall: Execute error");
435
        }
436
    }
437
    else
438
    {
439
        Error("getPackagesforBaseInstall:Prepare failure" );
440
    }
441
 
442
}
443
 
444
 
445
#-------------------------------------------------------------------------------
446
# Function        : getPackages_by_osid
447
#
448
# Description     : Get all the packages used by a given os_id
449
#
450
# Inputs          :
451
#
452
# Returns         :
453
#
454
 
455
my $count = 0;
456
sub getPackages_by_osid
457
{
458
    my ($os_id) =@_;
459
    my $foundDetails = 0;
460
    my (@row);
461
 
462
    connectDM(\$DM_DB) unless ($DM_DB);
463
 
464
    # First get details from pv_id
465
 
466
    my $m_sqlstr = "SELECT osc.*, pkg.pkg_name, pv.pkg_version, pd.IS_REJECTED, pv.IS_PATCH,pv.IS_OBSOLETE, pkg.pkg_id, pv.pv_id" .
467
                " FROM PACKAGES pkg, PACKAGE_VERSIONS pv,PRODUCT_DETAILS pd,".
468
	            "(" .
469
		        " SELECT osc.seq_num, osc.prod_id".
470
		        " FROM os_contents osc".
471
		        " WHERE osc.os_id = $os_id" .
472
	            " ) osc" .
473
                " WHERE pd.PROD_ID (+)= pv.PV_ID" .
474
                "   AND pv.pkg_id = pkg.pkg_id" .
475
                "   AND osc.PROD_ID = pv.pv_id" .
476
                " ORDER BY osc.SEQ_NUM desc" ;
477
 
478
    my $sth = $DM_DB->prepare($m_sqlstr);
479
    if ( defined($sth) )
480
    {
481
        if ( $sth->execute( ) )
482
        {
483
            if ( $sth->rows )
484
            {
485
                while ( @row = $sth->fetchrow_array )
486
                {
487
next if ( $opt_test && ++$count > 4 );
488
                    Verbose ("SBOM Package:".join (',',@row) );
489
                    my $pv_id =     $row[8];
490
                    my $name =      $row[2]  || 'BadName';
491
                    my $ver =       $row[3]  || 'BadVer';
492
 
493
                    $pv_id{$pv_id}{pkg_name} =$name;
494
                    $pv_id{$pv_id}{pkg_ver} = $ver;
495
                    $pv_id{$pv_id}{os_id}{$os_id} = 1;
496
                }
497
            }
498
            $sth->finish();
499
        }
500
    }
501
    else
502
    {
503
        Error("getPackages_by_osid:Prepare failure" );
504
    }
505
}
506
 
507
#-------------------------------------------------------------------------------
508
# Function        : getPkgDetailsByPV_ID
509
#
510
# Description     : Populate the Packages structure given a PV_ID
511
#                   Called for each package in the SBOM
512
#
513
# Inputs          : PV_ID           - Package Unique Identifier
514
#
515
# Returns         : Populates Package
516
#
517
sub getPkgDetailsByPV_ID
518
{
519
    my ($PV_ID) = @_;
520
    my $foundDetails = 0;
521
    my (@row);
522
 
523
    connectRM(\$RM_DB) unless ($RM_DB);
524
 
525
    # First get details from pv_id
526
 
527
    my $m_sqlstr = "SELECT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION, pv.PKG_LABEL, pv.SRC_PATH, pv.IS_DEPLOYABLE, pbi.BSA_ID, pbi.BM_ID" .
528
                    " FROM PACKAGE_VERSIONS pv, PACKAGES pkg, PACKAGE_BUILD_INFO pbi" .
529
                    " WHERE pv.PV_ID = \'$PV_ID\' AND pv.PKG_ID = pkg.PKG_ID AND pv.PV_ID = pbi.PV_ID (+) ";
530
 
531
    my $sth = $RM_DB->prepare($m_sqlstr);
532
    if ( defined($sth) )
533
    {
534
        if ( $sth->execute( ) )
535
        {
536
            if ( $sth->rows )
537
            {
538
                while ( @row = $sth->fetchrow_array )
539
                {
540
                    my $pv_id       = $row[0];
541
                    my $name        = $row[1];
542
                    my $ver         = $row[2];
543
                    my $label       = $row[3] || '';
544
                    my $path        = $row[4] || '';
545
                    my $deployable  = $row[5];
546
                    my $build_info  = $row[6] || '';
547
                    my $build_mach  = $row[7] || '';
548
 
549
                    #
550
                    #   BSA_ID: 1:debug, 2:prod, 3:debug+prod, 4:Java1.4 5: Java 1.5
551
                    #   BM_ID : 1:solaris, 2:win32, 3: linux, 4:generic
552
                    #
553
 
554
 
555
                    #
556
                    #   Does it look like a patch
557
                    #   We may want to ignore it.
558
                    #
559
                    my $patch = "";
560
                    unless ( $opt_patch )
561
                    {
562
                        if ( $ver =~ m~\.p\d+.\w+$~ )
563
                        {
564
                            $patch = "Patch";
565
                            $patch{$name} = 0
566
                                unless (  exists $patch{$name} );
567
                            $patch{$name}++;
568
                        }
569
                    }
570
                    Verbose ("getPkgDetailsByPV_ID: $PV_ID, $name, $ver, $build_mach ,$build_info, $patch");
571
                    next if ( $patch );
572
 
573
 
574
                    if ( exists $ignore{$name} )
575
                    {
576
                        Verbose2( "    Ignoring: $PV_ID, $name, $ver, $build_mach ,$build_info, $patch\n");
577
                        $ignore{$name}++;
578
                        last;
579
                    }
580
 
581
                    $path =~ tr~\\/~/~;                     # Don't compress multiples
582
 
583
                    $Package{$name}{$ver}{pvid} = $PV_ID;
584
                    $Package{$name}{$ver}{done} = 1;
585
                    $Package{$name}{$ver}{base} = 1;
586
                    $Package{$name}{$ver}{deployable} = 1 if ($deployable);
587
                    $Package{$name}{$ver}{label} = $label;
588
                    $Package{$name}{$ver}{path} = $path;
589
                    $Package{$name}{$ver}{build}{$build_mach} = $build_info if $build_mach;
590
 
591
                    GetDepends( $pv_id, $name, $ver );
592
 
593
                }
594
            }
595
            else
596
            {
597
                Warning ("No Package details for: PVID: $PV_ID");
598
            }
599
            $sth->finish();
600
        }
601
        else
602
        {
603
            Error("getPkgDetailsByPV_ID: Execute failure", $m_sqlstr );
604
        }
605
    }
606
    else
607
    {
608
        Error("Prepare failure" );
609
    }
610
}
611
 
612
#-------------------------------------------------------------------------------
613
# Function        : GetDepends
614
#
615
# Description     : Extract the dependancies for a given package version
616
#
617
# Inputs          : $pvid
618
#
619
# Returns         :
620
#
621
sub GetDepends
622
{
623
    my ($pv_id, $pname, $pver ) = @_;
624
 
625
    connectRM(\$RM_DB) unless ($RM_DB);
626
 
627
    #
628
    #   Now extract the package dependacies
629
    #
630
    my $m_sqlstr = "SELECT pkg.PKG_NAME, pv.PKG_VERSION, pd.DPV_ID" .
631
                   " FROM PACKAGE_DEPENDENCIES pd, PACKAGE_VERSIONS pv, PACKAGES pkg" .
632
                   " WHERE pd.PV_ID = \'$pv_id\' AND pd.DPV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID";
633
    my $sth = $RM_DB->prepare($m_sqlstr);
634
    if ( defined($sth) )
635
    {
636
        if ( $sth->execute( ) )
637
        {
638
            if ( $sth->rows )
639
            {
640
                my %depends;
641
                while ( my @row = $sth->fetchrow_array )
642
                {
643
#print "$pname $pver ===== @row\n";
644
                    my $name = $row[0];
645
                    my $ver = $row[1];
646
 
647
                    Verbose2( "       Depends: $name, $ver");
648
 
649
                    $depends{$name,$ver} = 1;
650
                    $Package{$name}{$ver}{usedby}{$pname,$pver} = 1;
651
 
652
                    unless ( exists $Package{$name}{$ver}{done} )
653
                    {
654
                        my @DATA = ($name, $ver, $row[2]);
655
                        push @StrayPackages, \@DATA;
656
                    }
657
                }
658
                $Package{$pname}{$pver}{depends} = \%depends;
659
            }
660
            $sth->finish();
661
        }
662
    }
663
    else
664
    {
665
        Error("GetDepends:Prepare failure" );
666
    }
667
}
668
 
669
#-------------------------------------------------------------------------------
670
# Function        : getPkgDetailsByRTAG_ID
671
#
672
# Description     : Extract all the packages for a given rtag_id
673
#
674
# Inputs          : RTAG_ID
675
#
676
# Returns         : 
677
#
678
 
679
sub getPkgDetailsByRTAG_ID
680
{
681
    my ($RTAG_ID) =@_;
682
    my $foundDetails = 0;
683
    my (@row);
684
 
685
    connectRM(\$RM_DB);
686
 
687
    # First get details from pv_id
688
 
689
    my $m_sqlstr = "SELECT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION".
690
                   " FROM RELEASE_CONTENT rc, PACKAGE_VERSIONS pv, PACKAGES pkg" .
691
                   " WHERE rc.RTAG_ID = $RTAG_ID AND rc.PV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID";
692
    my $sth = $RM_DB->prepare($m_sqlstr);
693
    if ( defined($sth) )
694
    {
695
        if ( $sth->execute( ) )
696
        {
697
            if ( $sth->rows )
698
            {
699
                while ( @row = $sth->fetchrow_array )
700
                {
701
                    my $pv_id   = $row[0];
702
                    my $name    = $row[1];
703
                    my $ver     = $row[2];
704
                    Verbose ("getPkgDetailsByRTAG_ID: $RTAG_ID, $name, $ver, $pv_id");
705
 
706
                    $Release{$name}{$ver}{pv_id} = $pv_id;
707
                    $Release_pvid{$pv_id} = 1;
708
                }
709
            }
710
            $sth->finish();
711
        }
712
    }
713
    else
714
    {
715
        Error("getPkgDetailsByRTAG_ID:Prepare failure" );
716
    }
717
}
718
 
719
 
720
#-------------------------------------------------------------------------------
721
# Function        : LocateStrays
722
#
723
# Description     : Locate stray packages
724
#                   These are packages that have not been defined by the
725
#                   top level SBOM. These are not really stray, although if the
726
#                   escrow is based on a RELEASE, then they will be packages
727
#                   from outside the release.
728
#
729
# Inputs          :
730
#
731
# Returns         :
732
#
733
sub LocateStrays
734
{
735
    while ( $#StrayPackages >= 0 )
736
    {
737
        my $DATA = pop @StrayPackages;
738
        my $name = $DATA->[0];
739
        my $ver = $DATA->[1];
740
        my $pv_id = $DATA->[2];
741
 
742
        next if ( exists $Package{$name}{$ver}{done} );
743
#print "Stray: $pv_id, $name, $ver\n";
744
        getPkgDetailsByPV_ID ( $pv_id );
745
        $Package{$name}{$ver}{stray} = 1;
746
    }
747
}
748
 
749
#-------------------------------------------------------------------------------
750
# Function        : BuildOrder
751
#
752
# Description     : Determine the order to build packages
753
#
754
# Inputs          :
755
#
756
# Returns         :
757
#
758
sub BuildOrder
759
{
760
    foreach my $name ( keys %Package )
761
    {
762
        foreach my $ver ( keys %{$Package{$name}} )
763
        {
764
            AddToBuildList( $name, $ver, $Package{$name}{$ver}{depends} );
765
        }
766
    }
767
 
768
    DetermineBuildOrder();
769
}
770
 
771
#-------------------------------------------------------------------------------
772
# Function        : AddToBuildList
773
#
774
# Description     : Add packages to a build list
775
#
776
# Inputs          : PackageName
777
#                   PackageVersion
778
#                   Hash of dependancies
779
#
780
# Returns         :
781
#
782
my %BuildList;
783
sub AddToBuildList
784
{
785
    my ($name, $ver, $pdepends ) = @_;
786
 
787
    Warning ("Duplicate Package to build: $name, $ver") if exists $BuildList{$name,$ver};
788
 
789
    #
790
    #   Clone dependancies as we will destroy the list as we process data
791
    #
792
    my $ref;
793
    $ref = dclone ($pdepends ) if $pdepends;
794
    $BuildList{$name,$ver}{depends} = $ref;
795
}
796
 
797
#-------------------------------------------------------------------------------
798
# Function        : DetermineBuildOrder
799
#
800
# Description     : Determine the build order
801
#
802
# Inputs          :
803
#
804
# Returns         :
805
#
806
sub DetermineBuildOrder
807
{
808
 
809
    my $file = "${fpref}_buildinfo.txt";
810
    push @create_list, $file;
811
 
812
    open (BI, ">$file" )  || Error ("Cannot create $file");
813
 
814
#    DebugDumpData ("BuildList", \%BuildList); exit 1;
815
 
816
    my $more = 1;
817
    my $level = 0;
818
    while ( $more )
819
    {
820
        my @build;
821
        $level ++;
822
        $more = 0;
823
        foreach my $key ( keys %BuildList )
824
        {
825
            #
826
            #   Locate packges with no dependencies left
827
            #
828
            next if ( keys %{$BuildList{$key}{depends}} );
829
            push @build, $key;
830
        }
831
 
832
        foreach my $build ( @build )
833
        {
834
            $more = 1;
835
            delete $BuildList{$build};
836
            my ($name, $ver) = split $;, $build;
837
 
838
            my $label = $Package{$name}{$ver}{label} || '';
839
            my $path  = $Package{$name}{$ver}{path} || '';
840
            $Package{$name}{$ver}{buildorder}  = $level;
841
 
842
            printf BI "Build(%2d): %40s %15s %-55s %s\n", $level, $name, $ver, $label, $path;
843
        }
844
 
845
        #
846
        #   Delete dependencies
847
        #
848
        foreach my $key ( keys %BuildList )
849
        {
850
            foreach my $build ( @build )
851
            {
852
                delete $BuildList{$key}{depends}->{$build};
853
            }
854
        }
855
    }
856
    close BI;
857
}
858
 
859
#-------------------------------------------------------------------------------
860
# Function        : GenerateHTML
861
#
862
# Description     : Generate Dependency information
863
#                   Generate a nive HTML dependancy table
864
#                   Shows DependOn and UsedBy
865
# Inputs          :
866
#
867
# Returns         :
868
#
869
 
870
sub GenerateHTML
871
{
872
    my $td = '<td style="vertical-align: top;">' . "\n";
873
    my $td3 = '<td style="vertical-align: top;" colspan="3">' . "\n";
874
    my $tdr = '<td style="text-align: right;">';
875
 
876
    my $file = "${fpref}_depends.html";
877
    push @create_list, $file;
878
    open (DP, ">$file" )  || Error ("Cannot create $file");
879
 
880
    #
881
    #   Generate an index
882
    #
883
    print DP "<dl><dt><h1>Index</h1></dt>\n";
884
    print DP "<dd><a href=\"#Ignore\">Ignored Packages</a></dd>\n";
885
    print DP "<dd><a href=\"#Depend\">Dependency Info</a></dd>\n";
886
    print DP "<dd><a href=\"#Multi\">Multiple Package Version</a></dd>\n";
887
    print DP "<dd><a href=\"#NoBuild\">Packages that cannot be built</a></dd>\n";
888
    print DP "<dd><a href=\"#Excess\">Excess Packages from Release: $opt_rtag_id</a></dd>\n" if ( $opt_rtag_id && $opt_sbom_id );
889
    print DP "<dd><a href=\"#Stray\">Required Packages, not part of the release</a></dd>\n" if ( $opt_rtag_id && !$opt_sbom_id );
890
    print DP "<dd><a href=\"#Inconsistent\">Packages in the Release, with inconsistent dependencies</a></dd>\n" if ( $opt_rtag_id && !$opt_sbom_id );
891
 
892
    print DP "</dl>\n";
893
 
894
    #
895
    #   Ignored Packages
896
    #
897
    print DP "<h1><a name=\"Ignore\">Ignored Packages</a></h1>\n";
898
 
899
    print DP "The following package, and all dependents, have been ignored.<br><br>\n";
900
 
901
    foreach my $name ( sort keys %ignore )
902
    {
903
        print DP "$name: $ignore{$name} versions<br>\n";
904
    }
905
 
906
    unless ( $opt_patch )
907
    {
908
        print DP "The following package have patches that have been ignored.<br><br>\n";
909
        foreach my $name ( sort keys %patch )
910
        {
911
            print DP "$name: $patch{$name} patches<br>\n";
912
        }
913
    }
914
 
915
    #
916
    #   Dependency Information
917
    #
918
    print DP "<h1><a name=\"Depend\">Dependency Info</a></h1>\n";
919
 
920
    print DP "<table border=\"1\"><tbody>\n";
921
    print DP "<tr>\n";
922
    print DP "<th>Package Dependency</th>\n";
923
    print DP "<th>Package Used by</th>\n";
924
    print DP "<th>Build Info</th>\n";
925
    print DP "</tr>\n";
926
 
927
    foreach my $name ( sort keys %Package )
928
    {
929
        foreach my $ver ( sort keys %{$Package{$name}} )
930
        {
931
            print DP "<tr>\n";
932
            #
933
            #   Depends On info
934
            #
935
 
936
            print DP $td;
937
            my $anchor= "${name}_${ver}";
938
            my $tag = "usedby_${name}_${ver}";
939
            printf  DP "<dl><dt><a name=\"$anchor\"></a><a href=\"#$tag\">%s&nbsp;%s</a> Depends on:</dt>\n", $name, $ver;
940
            foreach my $depend ( sort keys %{$Package{$name}{$ver}{depends}} )
941
            {
942
                my ($dname, $dver) = split $;, $depend;
943
                my $tag = "${dname}_${dver}";
944
                printf  DP "    <dd><a href=\"#$tag\">%s&nbsp;%s</a></dd>\n", $dname, $dver;
945
            }
946
            print DP "</dl>\n";
947
            print DP "</td>\n";
948
 
949
 
950
            #
951
            #   Used By information
952
            #
953
            print DP $td;
954
            $anchor= "usedby_${name}_${ver}";
955
            $tag = "${name}_${ver}";
956
            printf  DP "<dl><dt><a name=\"$anchor\"></a><a href=\"#$tag\">%s&nbsp;%s</a> Used by:</dt>\n", $name, $ver;
957
            foreach my $depend ( sort keys %{$Package{$name}{$ver}{usedby}} )
958
            {
959
                my ($dname, $dver) = split $;, $depend;
960
                my $tag = "usedby_${dname}_${dver}";
961
                printf  DP "    <dd><a href=\"#$tag\">%s&nbsp;%s</a></dd>\n", $dname, $dver;
962
            }
963
            print DP "</dl>\n";
964
            print DP "</td>\n";
965
 
966
            #
967
            #   Build Info
968
            #
969
            print DP $td;
970
            print DP "<table>";
971
            my $stray = ( exists ($Package{$name}{$ver}{stray}) && $Package{$name}{$ver}{stray} );
972
 
973
            my $pv_id = $Package{$name}{$ver}{pvid} || 'No PVID';
974
            my $pv_id_ref = $rm_base . $pv_id;
975
               $pv_id_ref .= "&rtag_id=" . $opt_rtag_id if ($opt_rtag_id && !$stray);
976
            my $pv_id_str = "<a href=\"$pv_id_ref\">$pv_id</a>";
977
 
978
            printf DP "<tr>${tdr}Pvid:</td><td>%s</td></tr>\n", $pv_id_str;
979
            printf DP "<tr>${tdr}Label:</td><td>%s</td></tr>\n", $Package{$name}{$ver}{label} || 'NoneProvided';
980
            printf DP "<tr>${tdr}Path:</td><td>%s</td></tr>\n", $Package{$name}{$ver}{path}  || 'NoneProvided';
981
 
982
            my $order = 'Not Built';
983
            my @machs;
984
 
985
            if ( exists($Package{$name}{$ver}{build}) )
986
            {
987
                $order = $Package{$name}{$ver}{buildorder};
988
                @machs = sort keys %{$Package{$name}{$ver}{build}};
989
            }
990
            else
991
            {
992
                my $tag = "notbuilt_${name}_${ver}";
993
                $order = "<a href=\"#$tag\">Not Built</a>"
994
            }
995
 
996
            printf DP "<tr>${tdr}Build Order:</td><td>%s</td></tr>\n", $order;
997
 
998
            my $text = "Build:";
999
            foreach my $mach ( @machs )
1000
            {
1001
                my $type = $Package{$name}{$ver}{build}{$mach};
1002
                printf DP "<tr>${tdr}$text</td><td>%s&nbsp;%s</td></tr>\n", $BM_ID{$mach} || "Unknown, $mach", $BSA_ID{$type} || 'Unknown';
1003
                $text = '';
1004
            }
1005
 
1006
            my $pvid = $Package{$name}{$ver}{pvid};
1007
            $text = "Deployed:";
1008
            foreach my $osid ( sort keys %{ $pv_id{$pvid}{os_id}  } )
1009
            {
1010
                my $os_name = $os_id_list{$osid}{os_name};
1011
                my $node =    $os_id_list{$osid}{node_name};
1012
 
1013
                my $ref = $dm_base . $osid;
1014
                my $str = "<a href=\"$ref\">$node,($os_name)</a>";
1015
 
1016
 
1017
                printf DP "<tr>${tdr}$text</td><td>$str</td></tr>\n";
1018
                $text = '';
1019
            }
1020
 
1021
            if ( $stray )
1022
            {
1023
                printf DP "<tr>${tdr}Stray:</td><td>Package included indirectly</td></tr>\n";
1024
            }
1025
 
1026
 
1027
 
1028
            print DP "</table>";
1029
            print DP "</td>\n";
1030
 
1031
            #
1032
            #   End of Row
1033
            #
1034
            print DP "</tr>\n";
1035
        }
1036
    }
1037
    print DP "</tbody></table>\n";
1038
 
1039
 
1040
    #
1041
    #   Multiple versions of a package
1042
    #
1043
    print DP "<h1><a name=\"Multi\">Multiple Package Versions</a></h1>\n";
1044
    print DP "<table border=\"1\"><tbody>\n";
1045
    print DP "<tr>\n";
1046
    print DP "<th>Multiple Versions</th>\n";
1047
    print DP "</tr>\n";
1048
 
1049
    foreach my $name ( sort keys %Package )
1050
    {
1051
        my @versions = keys %{$Package{$name}};
1052
        next unless ( $#versions > 0 );
1053
        print DP "<tr>\n";
1054
        print DP $td;
1055
        printf  DP "<dl><dt>$name</a> Versions:<dt>\n";
1056
 
1057
        foreach my $ver ( sort @versions )
1058
        {
1059
            my $tag = "${name}_${ver}";
1060
            printf  DP "    <dd>";
1061
            printf  DP "<a href=\"#$tag\">%s&nbsp;%s</a>\n", $name, $ver;
1062
            print   DP " - Not in Release" if ($opt_rtag_id && $Package{$name}{$ver}{stray});
1063
            printf  DP "</dd>\n", $name, $ver;
1064
        }
1065
        print DP "</dl>\n";
1066
        print DP "</td>\n";
1067
        print DP "</tr>\n";
1068
    }
1069
    print DP "</tbody></table>\n";
1070
 
1071
 
1072
    #
1073
    #   Packages that cannot be built
1074
    #
1075
    print DP "<h1><a name=\"NoBuild\">Packages that cannot be built</a></h1>\n";
1076
    print DP "<table border=\"1\"><tbody>\n";
1077
    print DP "<tr>\n";
1078
    print DP "<th>Not Built</th>\n";
1079
    print DP "</tr>\n";
1080
 
1081
    foreach my $name ( sort keys %Package )
1082
    {
1083
        my @versions = keys %{$Package{$name}};
1084
        foreach my $ver ( sort @versions )
1085
        {
1086
            next unless exists($Package{$name}{$ver}{bad_extract});
1087
            my @reasons = @{$Package{$name}{$ver}{bad_extract}};
1088
 
1089
            print DP "<tr><dl>\n";
1090
            print DP $td;
1091
 
1092
            my $tag = "${name}_${ver}";
1093
            my $anchor = "notbuilt_${name}_${ver}";
1094
 
1095
            printf  DP "<dt><a name=\"$anchor\"></a><a href=\"#$tag\">%s&nbsp;%s</a></dt>\n", $name, $ver;
1096
            foreach my $reason ( @reasons )
1097
            {
1098
                print  DP "<dd>$reason</dd>\n";
1099
            }
1100
 
1101
 
1102
            print DP "</dl>\n";
1103
            print DP "</td>\n";
1104
            print DP "</tr>\n";
1105
 
1106
        }
1107
    }
1108
    print DP "</tbody></table>\n";
1109
 
1110
    #
1111
    #   Packages that are in a specified release, but not described by the SBOM
1112
    #
1113
    if ( $opt_rtag_id && $opt_sbom_id )
1114
    {
1115
        print DP "<h1><a name=\"Excess\">Excess Packages from Release: $opt_rtag_id</a></h1>\n";
1116
        print DP "<table border=\"1\"><tbody>\n";
1117
        print DP "<tr>\n";
1118
        print DP '<th colspan="3">Excess Packages</th>';
1119
        print DP "</tr>\n";
1120
 
1121
        print DP "<tr>\n";
1122
        print DP '<th>Package</th>';
1123
        print DP '<th>PVID</th>';
1124
        print DP '<th>Used Package</th>';
1125
        print DP "</tr>\n";
1126
 
1127
        my $were_found = 0;
1128
        my $not_found = 0;
1129
        foreach my $name ( sort keys %Release )
1130
        {
1131
            my @versions = keys %{$Release{$name}};
1132
            foreach my $ver ( sort @versions )
1133
            {
1134
                if (exists($Package{$name}{$ver}))
1135
                {
1136
                    $were_found++;
1137
                    next;
1138
                }
1139
                $not_found++;
1140
 
1141
                print DP "<tr>\n";
1142
                print DP $td;
1143
 
1144
                my $pv_id = $Release{$name}{$ver}{pv_id} || 'No PVID';
1145
                my $pv_id_ref = $rm_base . $pv_id . "&rtag_id=" . $opt_rtag_id;
1146
                my $pv_id_str = "<a href=\"$pv_id_ref\">$pv_id</a>";
1147
 
1148
                printf  DP "$name $ver ", $name, $ver;
1149
                print DP "</td>\n";
1150
 
1151
                print DP $td;
1152
                printf DP "Pvid: %s\n", $pv_id_str;
1153
                print DP "</td>\n";
1154
 
1155
                print DP $td;
1156
                my @pver = keys %{$Package{$name}};
1157
                if (@pver)
1158
                {
1159
                    printf  DP "<dl><dt> Uses Versions:<dt>\n";
1160
                    foreach my $ver ( sort @pver  )
1161
                    {
1162
                        my $tag = "${name}_${ver}";
1163
                        printf  DP "    <dd><a href=\"#$tag\">%s&nbsp;%s</a></dd>\n", $name, $ver;
1164
                    }
1165
                    print DP "</dl>\n";
1166
                }
1167
                else
1168
                {
1169
                    printf DP "No Versions of this package used\n"
1170
                }
1171
                print DP "</td>\n";
1172
 
1173
 
1174
                print DP "</tr>\n";
1175
            }
1176
        }
1177
 
1178
        print DP "<tr>\n";
1179
        print DP $td3;
1180
        print DP "Packages found in SBOM: $were_found";
1181
        print DP "</td>\n";
1182
        print DP "</tr>\n";
1183
 
1184
        print DP "<tr>\n";
1185
        print DP $td3;
1186
        print DP "Packages NOT found in SBOM: $not_found";
1187
        print DP "</td>\n";
1188
        print DP "</tr>\n";
1189
 
1190
        print DP "</tbody></table>\n";
1191
    }
1192
 
1193
    #
1194
    #   Packages that are strays
1195
    #   They are not top level packages in the release
1196
    #
1197
    if ( $opt_rtag_id && !$opt_sbom_id )
1198
    {
1199
        print DP "<h1><a name=\"Stray\">Required Packages, not part of the release</a></h1>\n";
1200
        print DP "<table border=\"1\"><tbody>\n";
1201
        print DP "<tr>\n";
1202
        print DP '<th colspan="3">Stray Packages</th>';
1203
        print DP "</tr>\n";
1204
 
1205
        print DP "<tr>\n";
1206
        print DP '<th>Inconsisient Package</th>';
1207
        print DP '<th>PVID</th>';
1208
        print DP '<th>Preferred Package</th>';
1209
        print DP "</tr>\n";
1210
 
1211
        foreach my $name ( sort keys %Package )
1212
        {
1213
 
1214
            my @versions = keys %{$Package{$name}};
1215
            foreach my $ver ( sort @versions )
1216
            {
1217
                unless (exists($Package{$name}{$ver}{stray}) && $Package{$name}{$ver}{stray} )
1218
                {
1219
                    next;
1220
                }
1221
 
1222
                #
1223
                #   Determine preferred package version(s)
1224
                #   These will be those without a 'stray' tag
1225
                #
1226
                my @preferred = ();
1227
                foreach my $pver ( keys %{$Package{$name}} )
1228
                {
1229
                    next if (exists($Package{$name}{$pver}{stray} ) && $Package{$name}{$pver}{stray} );
1230
                    push @preferred, $pver;
1231
                }
1232
 
1233
                print DP "<tr>\n";
1234
 
1235
                #
1236
                #  Package name and Used By information
1237
                #
1238
                print DP $td;
1239
                my $anchor= "usedby_${name}_${ver}";
1240
                my $tag = "${name}_${ver}";
1241
                printf  DP "<dl><dt><a name=\"$anchor\"></a><a href=\"#$tag\">%s&nbsp;%s</a> Used by:</dt>\n", $name, $ver;
1242
                foreach my $depend ( sort keys %{$Package{$name}{$ver}{usedby}} )
1243
                {
1244
                    my ($dname, $dver) = split $;, $depend;
1245
                    my $tag = "usedby_${dname}_${dver}";
1246
                    printf  DP "    <dd><a href=\"#$tag\">%s&nbsp;%s</a></dd>\n", $dname, $dver;
1247
                }
1248
                print DP "</dl>\n";
1249
                print DP "</td>\n";
1250
 
1251
 
1252
                my $pv_id = $Package{$name}{$ver}{pvid} || 'No PVID';
1253
 
1254
                my $pv_id_ref = $rm_base . $pv_id;
1255
                my $pv_id_str = "<a href=\"$pv_id_ref\">$pv_id</a>";
1256
 
1257
                print DP $td;
1258
                printf DP "Pvid: %s\n", $pv_id_str;
1259
                print DP "</td>\n";
1260
 
1261
                #
1262
                #   Insert Preferred package(s)
1263
                #
1264
                print DP $td;
1265
                print DP "<table>\n";
1266
                foreach my $pver ( sort @preferred )
1267
                {
1268
                    my $tag = "${name}_${pver}";
1269
                    printf  DP "<tr><td><a href=\"#$tag\">%s&nbsp;%s</a></td></tr>\n", $name, $pver;
1270
                }
1271
 
1272
                print DP "</table>\n";
1273
                print DP "</tr>\n";
1274
 
1275
            }
1276
        }
1277
 
1278
        print DP "</tbody></table>\n";
1279
    }
1280
 
1281
    #
1282
    #   Packages that have components not in the release
1283
    #   They are not top level packages in the release
1284
    #
1285
    if ( $opt_rtag_id && !$opt_sbom_id )
1286
    {
1287
        print DP "<h1><a name=\"Inconsistent\">Packages in the Release, with inconsistent dependencies</a></h1>\n";
1288
        print DP "<table border=\"1\"><tbody>\n";
1289
 
1290
        print DP "<tr>\n";
1291
        print DP '<th>Inconsisient Package</th>';
1292
        print DP "</tr>\n";
1293
 
1294
        foreach my $name ( sort keys %Package )
1295
        {
1296
 
1297
            my @versions = keys %{$Package{$name}};
1298
            foreach my $ver ( sort @versions )
1299
            {
1300
                #
1301
                #   Ignore 'stray' packages
1302
                #
1303
                next if (exists($Package{$name}{$ver}{stray}) && $Package{$name}{$ver}{stray} );
1304
 
1305
                #
1306
                #   Is it inconsitient
1307
                #
1308
                my $ok = 1;
1309
                foreach my $depend ( sort keys %{$Package{$name}{$ver}{depends}} )
1310
                {
1311
                    my ($dname, $dver) = split $;, $depend;
1312
                    if (exists($Package{$dname}{$dver}{stray}) && $Package{$dname}{$dver}{stray} )
1313
                    {
1314
                        $ok = 0;
1315
                        last;
1316
                    }
1317
                }
1318
 
1319
                next if ( $ok );
1320
 
1321
 
1322
                #
1323
                #   Depends On info
1324
                #
1325
 
1326
                print DP "<tr>\n";
1327
                print DP $td;
1328
                my $anchor= "${name}_${ver}";
1329
                my $tag = "usedby_${name}_${ver}";
1330
                printf  DP "<dl><dt><a name=\"$anchor\"></a><a href=\"#$tag\">%s&nbsp;%s</a> Inconsistent::</dt>\n", $name, $ver;
1331
                foreach my $depend ( sort keys %{$Package{$name}{$ver}{depends}} )
1332
                {
1333
                    my ($dname, $dver) = split $;, $depend;
1334
                    next unless (exists($Package{$dname}{$dver}{stray}) && $Package{$dname}{$dver}{stray} );
1335
 
1336
                    my $tag = "${dname}_${dver}";
1337
                    printf  DP "    <dd><a href=\"#$tag\">%s&nbsp;%s</a></dd>\n", $dname, $dver;
1338
                }
1339
                print DP "</dl>\n";
1340
                print DP "</td>\n";
1341
                print DP "<tr>\n";
1342
 
1343
            }
1344
        }
1345
 
1346
        print DP "</tbody></table>\n";
1347
    }
1348
 
1349
 
1350
 
1351
 
1352
    close DP;
1353
}
1354
 
1355
#-------------------------------------------------------------------------------
1356
# Function        : LeafTracking
1357
#
1358
# Description     : Determine leaf packages and the base packages
1359
#                   Create a CSV file containing leaf packages for
1360
#                   each package in the SBOM/Release
1361
#
1362
# Inputs          : 
1363
#
1364
# Returns         : 
1365
#
1366
my %leaf;
1367
sub LeafTracking
1368
{
1369
#    DebugDumpData ("Package", \%Package   );
1370
 
1371
    foreach my $name ( sort keys %Package )
1372
    {
1373
 
1374
        my @versions = keys %{$Package{$name}};
1375
        foreach my $ver ( sort @versions )
1376
        {
1377
            #
1378
            #   Only process leaf packages
1379
            #   These are packages that have no users
1380
            #
1381
            next if ( exists $Package{$name}{$ver}{usedby} );
1382
 
1383
            #
1384
            #   For each leaf package/version
1385
            #   Walk the dependancy tree and determine all of its dependents
1386
            #
1387
            my @plist = keys %{$Package{$name}{$ver}{depends}};
1388
            my %done;
1389
            while ( $#plist >= 0 )
1390
            {
1391
                my $depends = pop @plist;
1392
                next if ( exists $done{$depends} );
1393
                $done{$depends} = 1;
1394
 
1395
                my ($dname, $dver) = split $;, $depends;
1396
                $leaf{$name}{$ver}{depends}{$depends} = 1;
1397
 
1398
                my @dlist = keys %{$Package{$dname}{$dver}{depends}};
1399
                push @plist, @dlist;
1400
            }
1401
        }
1402
    }
1403
 
1404
    #
1405
    #   Now have a list of leaf packages and all dependent packages
1406
    #   Invert this, and create a list of leaf packages for each package
1407
    #
1408
    foreach my $name ( keys %leaf )
1409
    {
1410
        foreach my $ver ( keys %{$leaf{$name}} )
1411
        {
1412
            foreach my $depends ( keys %{$leaf{$name}{$ver}{depends}}  )
1413
            {
1414
                my ($dname, $dver) = split $;, $depends;
1415
                $Package{$dname}{$dver}{leaf}{$name,$ver} = 1;
1416
            }
1417
        }
1418
    }
1419
 
1420
    #
1421
    #   Put all the useful information into a CSV file
1422
    #
1423
    my $file = "${fpref}_affects.csv";
1424
    push @create_list, $file;
1425
    open (AF, ">$file" ) || Error("Cannot create $file");
1426
 
1427
    #
1428
    #   Create CSV header
1429
    #
1430
    my @header = ('Package', 'Version', 'In Release', 'Buildable', 'Build Machine');
1431
 
1432
    push @header, $BM_ID{$_} foreach sort keys %BM_ID;
1433
    push @header, 'Product List';
1434
 
1435
    print AF MakeCsv(@header),"\n";
1436
 
1437
    #
1438
    #   Create Body of the data
1439
    #
1440
    foreach my $name ( sort keys %Package )
1441
    {
1442
 
1443
        my @versions = keys %{$Package{$name}};
1444
        foreach my $ver ( sort @versions )
1445
        {
1446
            #
1447
            #   Only process leaf packages
1448
            #   These are packages that have no users
1449
            #
1450
            next unless ( exists $Package{$name}{$ver}{leaf} );
1451
            my @data = ($name, $ver);
1452
 
1453
            #
1454
            #   Flag stray packages
1455
            #
1456
            my $stray = ( exists ($Package{$name}{$ver}{stray}) && $Package{$name}{$ver}{stray} );
1457
            push @data, $stray ? 'N' : 'Y';
1458
 
1459
            #
1460
            #   Flag packages that have duff data such that it cannot be build
1461
            #
1462
            my $bad = exists($Package{$name}{$ver}{bad_extract});
1463
            push @data, $bad ? 'N' : 'Y';
1464
 
1465
            #
1466
            #   Flag auto-buildable
1467
            #   Really just a summary of the following fields. ie: build machine has been specified
1468
            #
1469
            my $buildable = exists($Package{$name}{$ver}{build});
1470
            push @data, $buildable ? 'Y' : 'N';
1471
 
1472
            #
1473
            #   Flag build machines
1474
            #
1475
            foreach my $bmid ( sort keys %BM_ID )
1476
            {
1477
                my $build = exists ($Package{$name}{$ver}{build}{$bmid} );
1478
                push @data, $build ? 'Y' : 'N';
1479
            }
1480
 
1481
            #
1482
            #   Leaf packages
1483
            #
1484
            my @plist = sort keys %{$Package{$name}{$ver}{leaf}};
1485
            foreach my $depends ( @plist )
1486
            {
1487
                my ($dname, $dver) = split $;, $depends;
1488
                push @data, "$dname $dver";
1489
            }
1490
 
1491
            #
1492
            #   Print as a CSV entry
1493
            #
1494
            print AF MakeCsv(@data),"\n";
1495
        }
1496
    }
1497
    close AF;
1498
 
1499
#    DebugDumpData ("Package", \%Package   );
1500
#    DebugDumpData ("Leaf", \%leaf );
1501
#    Error ("DONE");
1502
}
1503
 
1504
sub MakeCsv
1505
{
1506
    my $string;
1507
    my $pad = '';
1508
    foreach  ( @_ )
1509
    {
1510
        next unless ( $_ );
1511
        $string .= $pad . '"' . $_ . '"';
1512
        $pad = ',';
1513
    }
1514
    return $string;
1515
}
1516
 
1517
#-------------------------------------------------------------------------------
1518
# Function        : extract_files
1519
#
1520
# Description     : Alternate mode of operation
1521
#                   Extract files from the generated list. This is intended to
1522
#                   be run as a sperate phase taking the 'extract' file
1523
#
1524
# Inputs          :
1525
#
1526
# Returns         : 
1527
#
1528
sub extract_files
1529
{
1530
    my @extract_order;
1531
    my %extract;
1532
    ErrorConfig( 'name'    => 'ESCROW-EXTRACT' );
1533
 
1534
    #
1535
    #   Open the file and read in data in one hit
1536
    #   This will detect file errors early
1537
    #
1538
    Error ("Cannot find specified file: $opt_extract")
1539
        unless ( -f $opt_extract );
1540
 
1541
    open (FH, "<$opt_extract" ) || Error ("Cannot open file");
1542
    while ( <FH> )
1543
    {
1544
        s~[\r\n]+$~~;
1545
        next unless ( $_ );
1546
 
1547
        my ($view, $label, $path);
1548
        foreach ( split ' ', $_ )
1549
        {
1550
            if ( m~^-view=(.+)~ ) {
1551
                $view = $1;
1552
            } elsif ( m~^-label=(.+)~ ) {
1553
                $label = $1;
1554
            } elsif ( m~^-path=(.+)~ ) {
1555
                $path = $1;
1556
            }
1557
        }
1558
 
1559
        Error "Duplicate view name: $view" if ( exists $extract{$view} );
1560
        Error "Bad file format in line: $_" unless ( $view && $label );
1561
        push @extract_order, $view;
1562
        $extract{$view}{label} = $label;
1563
        $extract{$view}{path} = $path;
1564
    }
1565
    close FH;
1566
 
1567
    #
1568
    #   Log the file processing
1569
    #
1570
    my $lfile = "${opt_extract}.log";
1571
    open (FH, ">$lfile" ) || Error ("Cannot open log file: $lfile");
1572
 
1573
    #
1574
    #   Process each entry
1575
    #
1576
    foreach my $view ( @extract_order )
1577
    {
1578
        my $label = $extract{$view}{label};
1579
        my $path = $extract{$view}{path};
1580
        my $rv = JatsCmd ("extract", "-extractfiles", "-view=$view", "-label=$label", "-path=$path", "-root=.", "-noprefix");
1581
        print FH "$view : SUCCESS\n" unless $rv;
1582
        print FH "$view : ERROR\n" if $rv;
1583
    }
1584
    close FH;
1585
 
1586
}
1587
 
1588
 
1589
#-------------------------------------------------------------------------------
1590
#   Documentation
1591
#
1592
 
1593
=pod
1594
 
1595
=head1 NAME
1596
 
1597
escrow - Extract Escrow Build Information
1598
 
1599
=head1 SYNOPSIS
1600
 
1601
  jats escrow [options]
1602
 
1603
 Options:
1604
    -help              - brief help message
1605
    -help -help        - Detailed help message
1606
    -man               - Full documentation
1607
    -sbomid=xxx        - Specify the SBOM to process
1608
    -rtagid=xxx        - Specify the Release to process (Optional)
1609
    -ignore=name       - Ignore packages with the specified name
1610
    -extract=fname     - Extract files from a previous run
1611
    -verbose           - Enable verbose output
1612
    -[no]patch         - Ignore/Include patches. Default:Include
1613
    -[no]test          - Reduced package scanning for test
1614
 
1615
=head1 OPTIONS
1616
 
1617
=over 8
1618
 
1619
=item B<-help>
1620
 
1621
Print a brief help message and exits.
1622
 
1623
=item B<-help -help>
1624
 
1625
Print a detailed help message with an explanation for each option.
1626
 
1627
=item B<-man>
1628
 
1629
Prints the manual page and exits.
1630
 
1631
=item B<-sbomid=xxx>
1632
 
1633
This option is mandatory. It specifies the SBOM to process. The sbomid must be
1634
determined from Deployment Manager.
1635
 
1636
=item B<-rtagid=xxx>
1637
 
1638
If provided, this option specifies an RTAG_ID to process in conjunction with the SBOM.
1639
The RTAG_ID must be determined from Release Manager. The program will determine
1640
packages that are in th Release, but not in the SBOM.
1641
 
1642
=item B<-ignore=name>
1643
 
1644
All versions of the named package will be ignored. This parameter is options.
1645
It may be used multiple times.
1646
 
1647
=item B<-extract=name>
1648
 
1649
This option will process the 'extract' file created in a previous run of this
1650
program and extract source files for the package-versions found in the file.
1651
 
1652
The command will then create a log file recording packages that could ne be
1653
extracted.
1654
 
1655
This option cannot be used in conjunction wit the -rtagid or -sbomid.
1656
 
1657
=item B<-[no]patch>
1658
 
1659
This option is used ignore patches. If -nopatch is selected, then packages
1660
versions that look like a patch will be added to the ignore list.
1661
 
1662
=item B<-[no]test>
1663
 
1664
This option is used for testing. It will only process the first two OS entries
1665
in the SBOM. This speeds up processing. It does not generate a complete list of
1666
packages.
1667
 
1668
=item B<verbose>
1669
 
1670
This option will display progress information as the program executes.
1671
 
1672
=back
1673
 
1674
=head1 DESCRIPTION
1675
 
1676
This program is a tool for extracting Escrow build information.
1677
 
1678
Given an SBOM_ID this program will:
1679
 
1680
=over 8
1681
 
1682
=item * Determine all the NODES in the SBOM
1683
 
1684
=item * Determine all the Base Packages for each NODE
1685
 
1686
=item * Determine all the Packages for each NODE
1687
 
1688
=item * Determine all the dependent packages for all packages encountered
1689
 
1690
=item * Generate a list of jats commands to extract the package source
1691
 
1692
=item * Generate a file describing the build order
1693
 
1694
=item * Generate a file describing the packages that cannot be built
1695
 
1696
=item * Generate a CSV file containing build information and leaf packages.
1697
 
1698
This file contains list of packages that are ultimately affected by the package.
1699
This list does not include intermediate packages; only the deployed packages.
1700
 
1701
=item * Generate an HTML file with extensive cross reference information
1702
 
1703
=over 8
1704
 
1705
=item * List of all packages with references into Release Manager
1706
 
1707
=item * List of all packages showing dependent packages
1708
 
1709
=item * List of all packages showing consumer packages
1710
 
1711
=item * List of all packages for which multiple versions are required
1712
 
1713
=item * Details of packages that are not built.
1714
 
1715
=item * Build order
1716
 
1717
=item * Build machines and built types
1718
 
1719
=item * Deployed target nodes, with references into deployment manager
1720
 
1721
=back
1722
 
1723
=back
1724
 
1725
Given an RTAG_ID the program will do similar work, but the list of packages will be
1726
limited to the one release.
1727
 
1728
This may take some time, as a typical escrow build may contain many hundreds of packages.
1729
 
1730
The program will display a list of files that have been created.
1731
 
1732
Given an 'extract' file from a previous run of this program the program will:
1733
 
1734
=over 8
1735
 
1736
=item * Parse the 'extract' file
1737
 
1738
=item * Create subdirectoroes for each package version within the file.
1739
 
1740
This is done in such a way that no views are left in place.
1741
 
1742
=item * Create a log file showing packages that could not be extracted.
1743
 
1744
=back
1745
 
1746
=cut
1747