xref: /AOO41X/main/solenv/bin/modules/installer/packagepool.pm (revision ef1ef8e674fabf3a541d12c6e6c14cecdfc2f9e7)
1#*************************************************************************
2#
3# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4#
5# Copyright 2000, 2010 Oracle and/or its affiliates.
6#
7# OpenOffice.org - a multi-platform office productivity suite
8#
9# This file is part of OpenOffice.org.
10#
11# OpenOffice.org is free software: you can redistribute it and/or modify
12# it under the terms of the GNU Lesser General Public License version 3
13# only, as published by the Free Software Foundation.
14#
15# OpenOffice.org is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU Lesser General Public License version 3 for more details
19# (a copy is included in the LICENSE file that accompanied this code).
20#
21# You should have received a copy of the GNU Lesser General Public License
22# version 3 along with OpenOffice.org.  If not, see
23# <http://www.openoffice.org/license.html>
24# for a copy of the LGPLv3 License.
25#
26#*************************************************************************
27
28package installer::packagepool;
29
30use Digest::MD5;
31use installer::exiter;
32use installer::globals;
33use installer::logger;
34use installer::pathanalyzer;
35use installer::worker;
36
37######################################################
38# Checking the md5sum of a file
39######################################################
40
41sub get_md5sum
42{
43    my ($filename) = @_;
44
45    open(FILE, "<$filename") or die "ERROR: Can't open $filename for creating file hash";
46    binmode(FILE);
47    my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest;
48    close(FILE);
49
50    return $digest;
51}
52
53####################################################
54# Setting a unique sessionid to identify this
55# packaging process.
56####################################################
57
58sub set_sessionid
59{
60    my $pid = $$;       # process id
61    my $timer = time(); # time
62    $installer::globals::sessionid = $pid . $timer;
63    $installer::globals::sessionidset = 1;
64    my $infoline = "\nPool: Setting session id: $installer::globals::sessionid.\n";
65    push( @installer::globals::logfileinfo, $infoline);
66}
67
68####################################################
69# Setting and creating pool path.
70####################################################
71
72sub set_pool_path
73{
74    $installer::globals::unpackpath =~ s/\Q$installer::globals::separator\E\s*$//;  # removing ending slashes and backslashes
75    $installer::globals::poolpath = $installer::globals::unpackpath . $installer::globals::separator . "pool_" . $installer::globals::packageformat;
76    installer::systemactions::create_directory($installer::globals::poolpath);
77    $installer::globals::poolpathset = 1;
78}
79
80####################################################
81# Comparing the content of two epm files.
82####################################################
83
84sub compare_epm_content
85{
86    my ($oldcontent, $newcontent) = @_;
87
88    my $identical = 1;
89    my $diffinfo = "";
90
91    # Removing empty lines and files from $newcontent
92
93    my @newlocalcontent = ();
94    for ( my $i = 0; $i <= $#{$newcontent}; $i++ )
95    {
96        if ( ${$newcontent}[$i] =~ /^\s*$/ ) { next; } # Removing empty lines from $newcontent. Empty lines are also not included into pcf file, from where $oldcontent was read.
97        if ( ${$newcontent}[$i] =~ /^\s*f\s+/ ) { next; } # Ignoring files, they can contain temporary pathes
98        if (( ${$newcontent}[$i] =~ /^\s*%readme\s+/ ) || ( ${$newcontent}[$i] =~ /^\s*%license\s+/ )) { next; } # ignoring license and readme (language specific!)
99        my $oneline = ${$newcontent}[$i];
100        $oneline =~ s/\s*$//; # Removing line ends. Also not included in old epm file, that is read from pcf file.
101        push(@newlocalcontent, $oneline);
102    }
103
104    my $oldmember = $#{$oldcontent} + 1;
105    my $newmember = $#newlocalcontent + 1;
106
107    # comparing the count
108    if ( $oldmember != $newmember )
109    {
110        $identical = 0;
111        installer::logger::print_message("\n...... changed length of EPM file\n");
112        $diffinfo = "Pool: EPM, different line count: old epm file: $oldmember, new epm file: $newmember\n";
113        push(@installer::globals::epmdifflist, $diffinfo);
114    }
115
116    # comparing the content line for line, so the order must not change
117
118    if ( $identical )
119    {
120        for ( my $i = 0; $i <= $#{$oldcontent}; $i++ )
121        {
122            if ( ${$oldcontent}[$i] ne $newlocalcontent[$i] )
123            {
124                $identical = 0;
125                my $line = $i + 1;
126                installer::logger::print_message("\n...... different content in EPM file\n");
127                $diffinfo = "Pool: EPM, line $line changed from \"${$oldcontent}[$i]\" to \"$newlocalcontent[$i]\".\n";
128                push(@installer::globals::epmdifflist, $diffinfo);
129                last;
130            }
131        }
132    }
133
134    return $identical;
135}
136
137####################################################
138# Comparing the content of two pcf files.
139####################################################
140
141sub compare_package_content
142{
143    my ($oldcontent, $newcontent) = @_;
144
145    my $identical = 1;
146    my $infoline = "";
147
148    my $oldmember = scalar keys %{$oldcontent};
149    my $newmember = scalar keys %{$newcontent};
150
151    # comparing the count
152
153    if ( $oldmember != $newmember )
154    {
155        # Logging the difference
156        $identical = 0;
157        installer::logger::print_message("\n...... different number of files in packages. New number: $newmember, old number: $oldmember\n");
158        $infoline = "Different number of files in packages. New number: $newmember, old number: $oldmember\n";
159        push(@installer::globals::pcfdiffcomment, $infoline);
160    }
161
162    # comparing the keys
163
164    if ( $identical )
165    {
166        my $first = 1;
167        my $start = "\n";
168        foreach my $dest ( keys %{$newcontent} )
169        {
170            if ( ! exists($oldcontent->{$dest}) )
171            {
172                $identical = 0;
173                installer::logger::print_message("$start...... file only in one package (A): $dest\n");
174                $infoline = "File only in existing pool package: $dest\n";
175                push(@installer::globals::pcfdiffcomment, $infoline);
176                if ( $first ) { $start = ""; }
177                $first = 0;
178            }
179        }
180
181        # collecting all differences
182        if ( ! $identical )
183        {
184            foreach my $dest ( keys %{$oldcontent} )
185            {
186                if ( ! exists($newcontent->{$dest}) )
187                {
188                    $identical = 0;
189                    installer::logger::print_message("$start...... file only in one package (B): $dest\n");
190                    $infoline = "File only in new package: $dest\n";
191                    push(@installer::globals::pcfdiffcomment, $infoline);
192                    if ( $first ) { $start = ""; }
193                    $first = 0;
194                }
195            }
196        }
197    }
198
199    # comparing the checksum
200
201    if ( $identical )
202    {
203        my $first = 1;
204
205        foreach my $dest ( keys %{$newcontent} )
206        {
207            if ( $newcontent->{$dest}->{'md5sum'} ne $oldcontent->{$dest}->{'md5sum'} )
208            {
209                $identical = 0;
210                if ( $first == 1 )
211                {
212                    installer::logger::print_message("\n");
213                    $first = 0;
214                }
215                $installer::globals::pcfdifflist{$dest} = 1;
216                installer::logger::print_message("...... different file: $dest\n");
217                # last;
218            }
219
220            if ( $installer::globals::iswindowsbuild )
221            {
222                if ( $newcontent->{$dest}->{'uniquename'} ne $oldcontent->{$dest}->{'uniquename'} )
223                {
224                    $identical = 0;
225                    $installer::globals::pcfdifflist{$dest} = 1;
226                    installer::logger::print_message("\n...... different file: $dest");
227                    # last;
228                }
229            }
230        }
231    }
232
233    return $identical;
234}
235
236####################################################
237# Calculating content of pcf file.
238####################################################
239
240sub calculate_current_content
241{
242    my ($filesarray, $packagename) = @_;
243
244    installer::logger::include_timestamp_into_logfile("\nCalculating content for package content file ($packagename), start");
245
246    my %globalcontent = ();
247
248    for ( my $i = 0; $i <= $#{$filesarray}; $i++ )
249    {
250        my %onefilehash = ();
251
252        my $onefile = ${$filesarray}[$i];
253        if ( ! $onefile->{'sourcepath'} ) { installer::exiter::exit_program("ERROR: No sourcepath found for file $onefile->{'gid'}", "calculate_current_content");  }
254        my $source = $onefile->{'sourcepath'};
255        if ( $onefile->{'zipfilesource'} ) { $source = $onefile->{'zipfilesource'}; }
256        if ( ! -f $source ) { installer::exiter::exit_program("ERROR: Sourcefile not found: $source ($onefile->{'gid'})", "calculate_current_content"); }
257
258        # For Windows the unique name inside the cabinet file also has to be saved
259        my $uniquename = "";
260        if ( $installer::globals::iswindowsbuild ) { $uniquename = $onefile->{'uniquename'};}
261
262        my $destination = $onefile->{'destination'};
263        my $checksum = get_md5sum($source);
264
265        $onefilehash{'md5sum'} = $checksum;
266        $onefilehash{'uniquename'} = $uniquename;
267
268        if ( exists($globalcontent{$destination}) ) { installer::exiter::exit_program("ERROR: Destination not unique: $destination ($onefile->{'gid'})", "calculate_current_content"); }
269        $globalcontent{$destination} = \%onefilehash;
270    }
271
272    installer::logger::include_timestamp_into_logfile("\nCalculating content for package content file ($packagename), start");
273
274    return \%globalcontent;
275}
276
277####################################################
278# Writing pcf file.
279####################################################
280
281sub create_pcfcontent_file
282{
283    my ($realpackagename, $md5sum, $filesize, $fullpackagename, $pkgversion, $epmfilecontent, $pcffilename) = @_;
284
285    my @content = ();
286    my $oneline = "PackageName: $realpackagename\n";
287    push(@content, $oneline);
288
289    $oneline = "md5sum: $md5sum\n";
290    push(@content, $oneline);
291
292    $oneline = "FileSize: $filesize\n";
293    push(@content, $oneline);
294
295    $oneline = "FullPackageName: $fullpackagename\n";
296    push(@content, $oneline);
297
298    $oneline = "PkgVersion: $pkgversion\n";
299    push(@content, $oneline);
300
301    foreach my $dest (keys %{$installer::globals::newpcfcontent} )
302    {
303        $oneline = "Files:\t$dest\t$installer::globals::newpcfcontent->{$dest}->{'md5sum'}\t$installer::globals::newpcfcontent->{$dest}->{'uniquename'}\n";
304        push(@content, $oneline);
305    }
306
307    for ( my $i = 0; $i <= $#{$epmfilecontent}; $i++ )
308    {
309        if ( ${$epmfilecontent}[$i] =~ /^\s*$/ ) { next; } # avoiding empty lines
310        if ( ${$epmfilecontent}[$i] =~ /^\s*f\s+/ ) { next; } # ignoring files, because they can contain temporary pathes
311        if (( ${$epmfilecontent}[$i] =~ /^\s*%readme\s+/ ) || ( ${$epmfilecontent}[$i] =~ /^\s*%license\s+/ )) { next; } # ignoring license and readme (language specific!)
312        $oneline = "EPM:\t${$epmfilecontent}[$i]";
313        push(@content, $oneline);
314    }
315
316    installer::files::save_file($pcffilename, \@content);
317}
318
319#######################################################
320# Reading the content of the package content file.
321#######################################################
322
323sub read_pcf_content
324{
325    my ($pcffilename) = @_;
326
327    my %allcontent = ();
328    my @epmfile = ();
329    my $realpackagename = "";
330
331    my $content = installer::files::read_file($pcffilename);
332
333    for ( my $i = 0; $i <= $#{$content}; $i++ )
334    {
335        my $line = ${$content}[$i];
336
337        if ( $line =~ /^\s*PackageName\:\s*(.*?)\s*$/ )
338        {
339            $realpackagename = $1;
340            $installer::globals::xpdpackageinfo{'RealPackageName'} = $realpackagename;
341            next;
342        }
343
344        if ( $line =~ /^\s*FullPackageName\:\s*(.*?)\s*$/ )
345        {
346            $installer::globals::xpdpackageinfo{'FullPackageName'} = $1;
347            next;
348        }
349
350        if ( $line =~ /^\s*FileSize\:\s*(.*?)\s*$/ )
351        {
352            $installer::globals::xpdpackageinfo{'FileSize'} = $1;
353            next;
354        }
355
356        if ( $line =~ /^\s*PkgVersion\:\s*(.*?)\s*$/ )
357        {
358            $installer::globals::xpdpackageinfo{'PkgVersion'} = $1;
359            next;
360        }
361
362        if ( $line =~ /^\s*md5sum\:\s*(.*?)\s*$/ )
363        {
364            $installer::globals::xpdpackageinfo{'md5sum'} = $1;
365            next;
366        }
367
368        if ( $line =~ /^\s*Files:\t(.+?)\t(.+?)\t(.*?)\s*$/ )
369        {
370            my $destination = $1;
371            my $checksum = $2;
372            my $uniquename = $3;
373
374            my %onefilehash = ();
375            $onefilehash{'md5sum'} = $checksum;
376            $onefilehash{'uniquename'} = $uniquename;
377
378            $allcontent{$destination} = \%onefilehash;
379            next;
380        }
381
382        if ( $line =~ /^\s*EPM:\t(.*?)\s*$/ )    # A line can be empty in epm file
383        {
384            my $epmcontent = $1;
385            push(@epmfile, $epmcontent);
386            next;
387        }
388    }
389
390    if ( $realpackagename eq "" ) { installer::exiter::exit_program("ERROR: Real package name not found in pcf file: \"$pcffilename\"", "read_pcf_content"); }
391
392    return ($realpackagename, \%allcontent, \@epmfile);
393}
394
395####################################################
396# Checking, if a specific package can be
397# created at the moment.
398####################################################
399
400sub check_package_availability
401{
402    my ($packagename) = @_;
403
404    my $package_is_available = 1;
405
406    my $checkfilename = $installer::globals::poolpath . $installer::globals::separator . $packagename . ".pcf.check";
407    my $lockfilename = $installer::globals::poolpath . $installer::globals::separator . $packagename . ".pcf.lock";
408
409    if (( -f $checkfilename ) || ( -f $lockfilename )) { $package_is_available = 0; }
410
411    return $package_is_available;
412}
413
414####################################################
415# Check, if the existence of the check or lock
416# file requires an exit of packaging process.
417####################################################
418
419sub check_pool_exit
420{
421    my ( $lockfilename, $timecounter ) = @_;
422
423    # How old is this lock file?
424    my $timeage = installer::logger::get_file_age($lockfilename);
425
426    # if ( $timeage > 1800 ) # file is older than half an hour
427    if ( $timeage > 3600 ) # file is older than an hour
428    {
429        my $timestring = installer::logger::convert_timestring($timeage);
430        my $infoline = "\nPool: Attention: \"$lockfilename\" is too old ($timestring). Removing file!\n";
431        installer::logger::print_message( "... $infoline" );
432        push( @installer::globals::logfileinfo, $infoline);
433        unlink $lockfilename;
434        # installer::exiter::exit_program("ERROR: Waiting too long for removal of lock file \"$lockfilename\"", "check_pool_exit (packagepool)");
435    }
436    else
437    {
438        my $filecontent = installer::files::read_file($lockfilename);
439        my $waittime = $timecounter * 10;
440        $waittime = installer::logger::convert_timestring($waittime);
441        my $infoline = "\nPool: Warning: \"$lockfilename\" blocks this process for $waittime. Lock content: \"${$filecontent}[0]\"\n";
442        installer::logger::print_message( "... $infoline" );
443        push( @installer::globals::logfileinfo, $infoline);
444    }
445}
446
447############################################################################
448# This function logs some information, that can be used to find
449# pool problems.
450############################################################################
451
452sub log_pool_info
453{
454    my ( $file_exists ) = @_;
455
456    my $infoline = "";
457
458    # Content saved in
459    # $installer::globals::savelockfilecontent = installer::files::read_file($filename);
460    # $installer::globals::savelockfilename = $filename;
461
462    if ( $file_exists )
463    {
464        $infoline = "\nPool Problem: Lock file \"$installer::globals::savelockfilename\" belongs to another process. This process has session id: $installer::globals::sessionid .\n";
465        push( @installer::globals::logfileinfo, $infoline);
466        $infoline = "Content of Lock file:\n";
467        push( @installer::globals::logfileinfo, $infoline);
468        foreach my $line ( @{$installer::globals::savelockfilecontent} ) { push( @installer::globals::logfileinfo, $line); }
469    }
470    else
471    {
472        $infoline = "\nPool Problem: Lock file \"$installer::globals::savelockfilename\" does not exist anymore (this process has session id: $installer::globals::sessionid) .\n";
473        push( @installer::globals::logfileinfo, $infoline);
474    }
475}
476
477############################################################################
478# Checking, if this process is the owner of the lock file in the pool.
479# This can be determined by the Process ID, that is written at the
480# beginning of the first line into the lock file.
481############################################################################
482
483sub process_is_owner
484{
485    my ( $filename ) = @_;
486
487    my $process_is_owner = 0;
488
489    $installer::globals::savelockfilecontent = installer::files::read_file($filename);
490    $installer::globals::savelockfilename = $filename;
491
492    if ( ${$installer::globals::savelockfilecontent}[0] =~ /^\s*\Q$installer::globals::sessionid\E\s+/ ) { $process_is_owner = 1; }
493
494    return $process_is_owner;
495}
496
497####################################################
498# Removing a package from installation set, if
499# there were pooling problems.
500####################################################
501
502sub remove_package_from_installset
503{
504    my ($newpackagepath) = @_;
505
506    my $infoline = "Pool problem: Removing package \"$newpackagepath\" from installation set!\n";
507    push(@installer::globals::logfileinfo, $infoline);
508
509    if ( -f $newpackagepath ) { unlink $newpackagepath; }
510    if ( -d $newpackagepath ) { installer::systemactions::remove_complete_directory($newpackagepath, 1); }
511
512    # Keeping the content of @installer::globals::installsetcontent up to date. Removing the last package.
513    pop(@installer::globals::installsetcontent);
514}
515
516####################################################
517# Check, if the package is in the pool and if
518# there are no changes in the package.
519####################################################
520
521sub package_is_up_to_date
522{
523    my ($allvariables, $onepackage, $packagename, $newepmcontent, $filesinpackage, $installdir, $subdir, $languagestringref) = @_;
524
525    installer::logger::print_message_without_newline( "... checking pool package $packagename ..." );
526
527    installer::logger::include_header_into_logfile("Checking package in pool: $packagename");
528
529    if ( ! $installer::globals::poolpathset ) { installer::packagepool::set_pool_path(); }
530    if ( ! $installer::globals::sessionidset ) { installer::packagepool::set_sessionid(); }
531
532    my $infoline = "";
533    # Resetting some variables for this package
534    my $package_is_up_to_date = 0;
535    my $realpackagename = "";
536    my $oldepmcontent = "";
537    my $waited_for_check = 0;
538    my $waited_for_lock = 0;
539    $installer::globals::newpcfcontentcalculated = 0;
540    %installer::globals::pcfdifflist = ();
541    @installer::globals::pcfdiffcomment = ();
542    @installer::globals::epmdifflist = ();
543
544    # Reading the package content file, if this file exists (extension *.pcf)
545    my $filename = $installer::globals::poolpath . $installer::globals::separator . $packagename . ".pcf";
546    my $checkfilename = $installer::globals::poolpath . $installer::globals::separator . $packagename . ".pcf.check";
547    my $lockfilename = $installer::globals::poolpath . $installer::globals::separator . $packagename . ".pcf.lock";
548    # Saving name in global variable, so that this file can be removed somewhere else (at the end of "put_content_into_pool").
549    $installer::globals::poolcheckfilename = $checkfilename;
550    $installer::globals::poollockfilename = $lockfilename;
551
552    my @checkfilecontent = ("$installer::globals::sessionid $installer::globals::product $$languagestringref $checkfilename");  # $$ is the process id
553    my @lockfilecontent = ("$installer::globals::sessionid $installer::globals::product $$languagestringref $lockfilename");    # $$ is the process id
554
555    # Waiting, step 1
556    # Checking, if another process checks this package at the moment
557    my $timecounter = 0;
558    while ( -f $checkfilename )
559    {
560        $timecounter++;
561
562        # including an exit to enable creation of other packages
563        if (( $timecounter == 1 ) && ( ! exists($installer::globals::poolshiftedpackages{$packagename}) ))
564        {
565            $package_is_up_to_date = 3; # repeat this package later
566            return $package_is_up_to_date;
567        }
568
569        $infoline = "Pool: $checkfilename exists. WAITING 10 seconds ($timecounter).\n";
570        if ( $timecounter == 1 ) { installer::logger::print_message( "\n" ); }
571        installer::logger::print_message( "... $infoline" );
572        push( @installer::globals::logfileinfo, $infoline);
573        # if ( $timecounter % 50 == 0 ) { check_pool_exit($checkfilename, $timecounter); }
574        if ( $timecounter % 100 == 0 ) { check_pool_exit($checkfilename, $timecounter); }
575        sleep 10; # process sleeps 10 seconds
576        $waited_for_check = 1;
577    }
578
579    # Creating file, showing that this package is checked at the moment by this process. No other process can reach this.
580    installer::files::save_file($checkfilename, \@checkfilecontent);    # Creating the Lock, to check this package. This blocks all other processes.
581    $installer::globals::processhaspoolcheckfile = 1;
582
583    # Check, if the Lock file creation was really successful
584    if ( ! -f $checkfilename )
585    {
586        $infoline = "Pool problem: Pool lock file \"$checkfilename\" could not be created successfully or was removed by another process (A)!\n";
587        push( @installer::globals::logfileinfo, $infoline);
588        log_pool_info(0);
589        $package_is_up_to_date = 4; # repeat this package
590        return $package_is_up_to_date;
591    }
592
593    if ( ! process_is_owner($checkfilename) )
594    {
595        $infoline = "Pool problem: Pool lock file \"$checkfilename\" belongs to another process (A)!\n";
596        push( @installer::globals::logfileinfo, $infoline);
597        log_pool_info(1);
598        $package_is_up_to_date = 4; # repeat this package
599        return $package_is_up_to_date;
600    }
601
602    $infoline = "Pool: Created file: $checkfilename\n";
603    push( @installer::globals::logfileinfo, $infoline);
604    if ( $waited_for_check ) { installer::logger::print_message( "... $infoline" ); }
605
606    # Waiting, step 2
607    # Checking, if another process creates this package at the moment
608    $timecounter = 0;
609    while ( -f $lockfilename )
610    {
611        $timecounter++;
612        $infoline = "Pool: $lockfilename exists. WAITING 10 seconds ($timecounter).\n";
613        if ( $timecounter == 1 ) { installer::logger::print_message( "\n" ); }
614        installer::logger::print_message( "... $infoline" );
615        push( @installer::globals::logfileinfo, $infoline);
616        # if ( $timecounter % 50 == 0 ) { check_pool_exit($lockfilename, $timecounter); }
617        if ( $timecounter % 100 == 0 ) { check_pool_exit($lockfilename, $timecounter); }
618        sleep 10; # process sleeps 10 seconds
619        $waited_for_lock = 1;
620    }
621
622    # No lock file exists, therefore no process creates this package at the moment. Check can be done now.
623    if ( $waited_for_lock ) { installer::logger::print_message( "... Pool: Proceeding, $lockfilename was removed.\n" ); }
624
625    my $package_already_exists = 0;
626
627    if ( -f $filename )
628    {
629        # Calculating content for pcf file
630        $installer::globals::newpcfcontent = calculate_current_content($filesinpackage, $packagename);
631        $installer::globals::newpcfcontentcalculated = 1;
632
633        # reading the existing pcf file
634        ($realpackagename, $oldpcfcontent, $oldepmcontent) = read_pcf_content($filename);
635
636        # First check: Package has to exist in pool (directories on Solaris)
637        my $fullpackage = $installer::globals::poolpath . $installer::globals::separator . $realpackagename;
638        if ( $installer::globals::issolarisbuild ) { $fullpackage = $fullpackage . ".tar"; }
639        if ( -f $fullpackage )
640        {
641            $package_already_exists = 1;
642            # Second check: Only files
643            my $content_is_identical = compare_package_content($oldpcfcontent, $installer::globals::newpcfcontent);
644
645            # Third check for Unix: Changes in the epm file?
646            if (( $content_is_identical ) && ( ! $installer::globals::iswindowsbuild ))
647            {
648                $content_is_identical = compare_epm_content($oldepmcontent, $newepmcontent);
649            }
650
651            if ( $content_is_identical ) { $package_is_up_to_date = 1; }
652        }
653    }
654
655    if ( $package_is_up_to_date )
656    {
657        $infoline = "Pool: $packagename: No new content, using existing package\n";
658        push( @installer::globals::logfileinfo, $infoline);
659        installer::logger::print_message( "... using package from pool\n" );
660    }
661    else
662    {
663        if ( $package_already_exists )
664        {
665            $infoline = "Pool: $packagename: Contains new content, creating new package. Differences:\n";
666            push( @installer::globals::logfileinfo, $infoline);
667            foreach my $dest ( sort keys %installer::globals::pcfdifflist ) { push( @installer::globals::logfileinfo, "$dest\n"); }
668            foreach my $dest ( @installer::globals::pcfdiffcomment ) { push( @installer::globals::logfileinfo, "$dest"); }
669            foreach my $dest ( @installer::globals::epmdifflist ) { push( @installer::globals::logfileinfo, "$dest"); }
670        }
671        else
672        {
673            $infoline = "Pool: $packagename: Does not exist in pool.\n";
674            push( @installer::globals::logfileinfo, $infoline);
675        }
676
677        installer::logger::print_message( "... packaging required\n" );
678        %installer::globals::xpdpackageinfo = (); # reset the filled hash, because the package cannot be used.
679
680        # Creating lock mechanism, so that other processes do not create this package, too.
681        installer::files::save_file($lockfilename, \@lockfilecontent);      # Creating the Lock, to create this package (Lock for check still exists).
682        $installer::globals::processhaspoollockfile = 1;
683
684        # Check if creation of Lock file was really successful
685
686        if ( ! -f $lockfilename )
687        {
688            $infoline = "Pool problem: Pool lock file \"$lockfilename\" could not be created successfully or was removed by another process (D)!\n";
689            push( @installer::globals::logfileinfo, $infoline);
690            log_pool_info(0);
691            $package_is_up_to_date = 4; # repeat this package
692            return $package_is_up_to_date;
693        }
694
695        if ( ! process_is_owner($lockfilename) )
696        {
697            $infoline = "Pool problem: Pool lock file \"$lockfilename\" belongs to another process (D)!\n";
698            push( @installer::globals::logfileinfo, $infoline);
699            log_pool_info(1);
700            $package_is_up_to_date = 4; # repeat this package
701            return $package_is_up_to_date;
702        }
703
704        $infoline = "Pool: Created file: $lockfilename\n";
705        push( @installer::globals::logfileinfo, $infoline);
706    }
707
708    my $newpackagepath = "";
709
710    if ( $package_is_up_to_date )
711    {
712        # Before the package is copied into the installation set, it has to be checked, if this process is really the owner of this lock file..
713        # Check, if lock file still exists and if this process is the owner.
714
715        if ( ! -f $checkfilename )
716        {
717            $infoline = "Pool problem: Pool lock file \"$checkfilename\" was removed by another process (B)!\n";
718            push( @installer::globals::logfileinfo, $infoline);
719            log_pool_info(0);
720            $package_is_up_to_date = 4; # repeat this package
721            return $package_is_up_to_date;
722        }
723
724        if ( ! process_is_owner($checkfilename) )
725        {
726            $infoline = "Pool problem: Pool lock file \"$checkfilename\" belongs to another process (B)!\n";
727            push( @installer::globals::logfileinfo, $infoline);
728            log_pool_info(1);
729            $package_is_up_to_date = 4; # repeat this package
730            return $package_is_up_to_date;
731        }
732
733        # Copying the package from the pool into the installation set
734        $newpackagepath = copy_package_from_pool($installdir, $subdir, $realpackagename);
735    }
736
737    # Before the lock file in the pool can be removed, it has to be checked, if this process is still the owner of this lock file.
738    # Check, if lock file still exists and if this process is the owner.
739    if ( ! -f $checkfilename )
740    {
741        $infoline = "Pool problem: Pool lock file \"$checkfilename\" was removed by another process (C)!\n";
742        push( @installer::globals::logfileinfo, $infoline);
743        log_pool_info(0);
744
745        # removing new package from installation set
746        if ( $newpackagepath ne "" ) { remove_package_from_installset($newpackagepath); }   # A file was copied and a problem occured with pooling
747
748        $package_is_up_to_date = 4; # repeat this package
749        return $package_is_up_to_date;
750    }
751
752    if ( ! process_is_owner($checkfilename) )
753    {
754        $infoline = "Pool problem: Pool lock file \"$checkfilename\" belongs to another process (C)!\n";
755        push( @installer::globals::logfileinfo, $infoline);
756        log_pool_info(1);
757
758        # removing new package from installation set
759        if ( $newpackagepath ne "" ) { remove_package_from_installset($newpackagepath); }   # A file was copied and a problem occured with pooling
760
761        $package_is_up_to_date = 4; # repeat this package
762        return $package_is_up_to_date;
763    }
764
765    # Removing the check file, releasing this package for the next process.
766    # The Lock to create this package still exists, if required.
767    unlink $checkfilename;
768    $installer::globals::processhaspoolcheckfile = 0;
769    $infoline = "Pool: Removing file: $checkfilename\n";
770    push( @installer::globals::logfileinfo, $infoline);
771
772    # Last chance before packaging starts, to check, if this process is really still owner
773    # of the packaging lock file. If not, this packaging process can be repeated.
774    if ( $installer::globals::processhaspoollockfile )
775    {
776        if ( ! -f $lockfilename )
777        {
778            $infoline = "Pool problem: Pool lock file \"$lockfilename\" was removed by another process (E)!\n";
779            push( @installer::globals::logfileinfo, $infoline);
780            log_pool_info(0);
781            $package_is_up_to_date = 4; # repeat this package
782            return $package_is_up_to_date;
783        }
784
785        if ( ! process_is_owner($lockfilename) )
786        {
787            $infoline = "Pool problem: Pool lock file \"$lockfilename\" belongs to another process (E)!\n";
788            push( @installer::globals::logfileinfo, $infoline);
789            log_pool_info(1);
790            $package_is_up_to_date = 4; # repeat this package
791            return $package_is_up_to_date;
792        }
793    }
794
795    # Collecting log information
796    if ( $package_is_up_to_date == 1 ) { $installer::globals::poolpackages{$packagename} = 1; }
797    if ( $package_is_up_to_date == 0 )
798    {
799        my @packreasons = ();
800        if ( $package_already_exists )
801        {
802            $infoline = "\t\tPool: $packagename: Contains new content, creating new package. Differences:\n";
803            push( @packreasons, $infoline);
804            foreach my $dest ( sort keys %installer::globals::pcfdifflist ) { push( @packreasons, "\t\t$dest\n"); }
805            foreach my $dest ( @installer::globals::pcfdiffcomment ) { push( @packreasons, "\t\t$dest"); }
806            foreach my $dest ( @installer::globals::epmdifflist ) { push( @packreasons, "\t\t$dest"); }
807        }
808        else
809        {
810            $infoline = "\t\tPool: $packagename: Does not exist in pool.\n";
811            push( @packreasons, $infoline);
812        }
813
814        $installer::globals::createpackages{$packagename} = \@packreasons;
815    }
816
817    return $package_is_up_to_date;
818}
819
820###################################################
821# Determine, which package was created newly
822###################################################
823
824sub determine_new_packagename
825{
826    my ( $dir ) = @_;
827
828    my ($newcontent, $allcontent) = installer::systemactions::find_new_content_in_directory($dir, \@installer::globals::installsetcontent);
829    @installer::globals::installsetcontent = ();
830    foreach my $element ( @{$allcontent} ) { push(@installer::globals::installsetcontent, $element); }
831
832    my $newentriesnumber = $#{$newcontent} + 1;
833    if ( $newentriesnumber > 1 )
834    {
835        my $newpackages = "";
836        foreach my $onepackage ( @{$newcontent} ) { $newpackages = $newpackages . " " . $onepackage; }
837        installer::exiter::exit_program("ERROR: More than one new package in directory $dir ($newpackages)", "determine_new_packagename (packagepool)");
838    }
839    elsif ( $newentriesnumber < 1 )
840    {
841        installer::exiter::exit_program("ERROR: No new package in directory $dir", "determine_new_packagename (packagepool)");
842    }
843    my $newpackage = ${$newcontent}[0];
844
845    return $newpackage;
846}
847
848####################################################
849# Including content into the package pool
850####################################################
851
852sub put_content_into_pool
853{
854    my ($packagename, $installdir, $subdir, $filesinpackage, $epmfilecontent) = @_;
855
856    my $infoline = "";
857
858    my $fullinstalldir = $installdir . $installer::globals::separator . $subdir;
859    my $fullrealpackagename = determine_new_packagename($fullinstalldir);
860    my $realpackagename = $fullrealpackagename;
861    installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$realpackagename);
862
863    installer::logger::include_header_into_logfile("Adding content into the package pool: $realpackagename (PackageName: $packagename)");
864
865    # Calculating content for pcf file, if not already done in "package_is_up_to_date"
866    if ( ! $installer::globals::newpcfcontentcalculated )
867    {
868        $installer::globals::newpcfcontent = calculate_current_content($filesinpackage, $packagename);
869        $installer::globals::newpcfcontentcalculated = 1;
870    }
871
872    # Determining md5sum and FileSize for the new package and saving in pcf file
873    my $md5sum = installer::xpdinstaller::get_md5_value($fullrealpackagename);
874    my $filesize = installer::xpdinstaller::get_size_value($fullrealpackagename);
875    my $fullpackagename = installer::xpdinstaller::get_fullpkgname_value($fullrealpackagename);
876    my $pkgversion = installer::xpdinstaller::get_pkgversion_value($fullrealpackagename);
877
878    # Put package content file (pcf) into pool
879    my $pcffilename = $installer::globals::poolpath . $installer::globals::separator . $packagename . ".pcf";
880    create_pcfcontent_file($realpackagename, $md5sum, $filesize, $fullpackagename, $pkgversion, $epmfilecontent, $pcffilename);
881
882    # Creating xpd info
883    $installer::globals::xpdpackageinfo{'FileSize'} = $filesize;
884    $installer::globals::xpdpackageinfo{'FullPackageName'} = $fullpackagename;
885    $installer::globals::xpdpackageinfo{'md5sum'} = $md5sum;
886    $installer::globals::xpdpackageinfo{'RealPackageName'} = $realpackagename;
887    $installer::globals::xpdpackageinfo{'PkgVersion'} = $pkgversion;
888
889    # Put package into pool
890    $infoline = "Pool: Adding package \"$packagename\" into pool.\n";
891    push( @installer::globals::logfileinfo, $infoline);
892
893    # Copying with unique name, containing PID. Only renaming if everything was fine.
894    my $realdestination = "";
895    my $uniquedestination = "";
896    if ( -f $fullrealpackagename )
897    {
898        $realdestination = $installer::globals::poolpath . $installer::globals::separator . $realpackagename;
899        $uniquedestination = $realdestination . "." . $installer::globals::sessionid;
900        installer::systemactions::copy_one_file($fullrealpackagename, $uniquedestination);
901    }
902
903    # Copying Solaris packages (as tar files)
904    if ( -d $fullrealpackagename )
905    {
906        my $tarfilename = $packagename . ".tar";
907        my $fulltarfilename = $fullinstalldir . $installer::globals::separator . $tarfilename;
908        my $size = installer::worker::tar_package($fullinstalldir, $packagename, $tarfilename, $installer::globals::getuidpath);
909        if (( ! -f $fulltarfilename ) || ( ! ( $size > 0 ))) { installer::exiter::exit_program("ERROR: Missing file: $fulltarfilename", "put_content_into_pool"); }
910        $realdestination = $installer::globals::poolpath . $installer::globals::separator . $tarfilename;
911        $uniquedestination = $realdestination . "." . $installer::globals::sessionid;
912        installer::systemactions::copy_one_file($fulltarfilename, $uniquedestination);
913        unlink $fulltarfilename;
914    }
915
916    # Before the new package is renamed in the pool, it has to be checked, if this process still has the lock for this package.
917    # Check, if lock file still exists and if this process is the owner. Otherwise a pool error occured.
918    if ( ! -f $installer::globals::poollockfilename )
919    {
920        unlink $uniquedestination;  # removing file from pool
921        log_pool_info(0);
922        installer::exiter::exit_program("ERROR: Pool lock file \"$installer::globals::poollockfilename\" was removed by another process (F)!", "put_content_into_pool");
923    }
924
925    if ( ! process_is_owner($installer::globals::poollockfilename) )
926    {
927        unlink $uniquedestination;  # removing file from pool
928        log_pool_info(1);
929        installer::exiter::exit_program("ERROR: Pool lock file \"$installer::globals::poollockfilename\" belongs to another process (F)!", "put_content_into_pool");
930    }
931
932    # Renaming the file in the pool (atomic step)
933    rename($uniquedestination, $realdestination);
934
935    $infoline = "Pool: Renamed file: \"$uniquedestination\" to \"$realdestination\".\n";
936    push( @installer::globals::logfileinfo, $infoline);
937
938    # Before the lock file in the pool can be removed, it has to be checked, if this process is still the owner of this lock file.
939    # Check, if lock file still exists and if this process is the owner. Otherwise a pool error occured.
940    if ( ! -f $installer::globals::poollockfilename )
941    {
942        log_pool_info(0);
943        installer::exiter::exit_program("ERROR: Pool lock file \"$installer::globals::poollockfilename\" was removed by another process (G)!", "put_content_into_pool");
944    }
945
946    if ( ! process_is_owner($installer::globals::poollockfilename) )
947    {
948        log_pool_info(1);
949        installer::exiter::exit_program("ERROR: Pool lock file \"$installer::globals::poollockfilename\" belongs to another process (G)!", "put_content_into_pool");
950    }
951
952    # Removing lock file, so that other processes can use this package now
953    unlink $installer::globals::poollockfilename;
954    $installer::globals::processhaspoollockfile = 0;
955    $infoline = "Pool: Removing file: $installer::globals::poollockfilename\n";
956    push( @installer::globals::logfileinfo, $infoline);
957}
958
959###################################################################
960# Copying a package from the pool into the installation set
961###################################################################
962
963sub copy_package_from_pool
964{
965    my ($installdir, $subdir, $packagename) = @_;
966
967    my $infoline = "Pool: Using package \"$packagename\" from pool.\n";
968    push( @installer::globals::logfileinfo, $infoline);
969    my $sourcefile = $installer::globals::poolpath . $installer::globals::separator . $packagename;
970    if ( $installer::globals::issolarisbuild ) { $sourcefile = $sourcefile . ".tar"; }
971    if ( ! -f $sourcefile ) { installer::exiter::exit_program("ERROR: Missing package in package pool: \"$sourcefile\"", "copy_package_from_pool"); }
972    my $destination = $installdir . $installer::globals::separator . $subdir;
973    if ( ! -d $destination ) { installer::systemactions::create_directory($destination); }
974    my $destinationfile = $destination . $installer::globals::separator . $packagename;
975    if ( $installer::globals::issolarisbuild ) { $destinationfile = $destinationfile . ".tar"; }
976    if ( -f $sourcefile ) { installer::systemactions::copy_one_file($sourcefile, $destinationfile); }
977    # Unpacking for Solaris
978    if ( $installer::globals::issolarisbuild )
979    {
980        my $tarfilename = $packagename . ".tar";
981        installer::worker::untar_package($destination, $tarfilename, $installer::globals::getuidpath);
982        unlink $destinationfile;
983        $destinationfile =~ s/.tar\s*$//;
984    }
985
986    # Keeping the content of @installer::globals::installsetcontent up to date (with full pathes):
987    push(@installer::globals::installsetcontent, $destinationfile);
988
989    return $destinationfile;
990}
991
992###################################################################
993# Counting keys in hash
994###################################################################
995
996sub get_count
997{
998    my ( $hashref ) = @_;
999
1000    my $counter = 0;
1001    foreach my $onekey ( keys %{$hashref} ) { $counter++; }
1002    return $counter;
1003}
1004
1005###################################################################
1006# Logging some pool information
1007###################################################################
1008
1009sub log_pool_statistics
1010{
1011    my $infoline = "";
1012
1013    installer::logger::include_header_into_logfile("Pool statistics:");
1014
1015    # Info collected in global hashes
1016    # %installer::globals::createpackages
1017    # %installer::globals::poolpackages
1018
1019    my $pool_packages = get_count(\%installer::globals::poolpackages);
1020    my $created_packages = get_count(\%installer::globals::createpackages);
1021
1022    $infoline = "Number of packages from pool: $pool_packages\n";
1023    push( @installer::globals::logfileinfo, $infoline);
1024
1025    foreach my $packagename ( sort keys(%installer::globals::poolpackages) )
1026    {
1027        $infoline = "\t$packagename\n";
1028        push( @installer::globals::logfileinfo, $infoline);
1029    }
1030
1031    $infoline = "\nNumber of packages that were created: $created_packages\n";
1032    push( @installer::globals::logfileinfo, $infoline);
1033
1034    foreach my $packagename ( sort keys(%installer::globals::createpackages) )
1035    {
1036        $infoline = "\t$packagename\n";
1037        push( @installer::globals::logfileinfo, $infoline);
1038        my $reason = $installer::globals::createpackages{$packagename};
1039
1040        for ( my $i = 0; $i <= $#{$reason}; $i++ )
1041        {
1042            $infoline = "${$reason}[$i]";
1043            push( @installer::globals::logfileinfo, $infoline);
1044        }
1045    }
1046}
1047
10481;
1049