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