File Coverage

blib/lib/Software/Packager/Aix.pm
Criterion Covered Total %
statement 368 476 77.3
branch 138 224 61.6
condition 7 17 41.1
subroutine 24 25 96.0
pod 6 6 100.0
total 543 748 72.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Software::Packager::Aix - The Software::Packager extension for AIX 4.1 and above
4              
5             =head1 SYNOPSIS
6              
7             use Software::Packager;
8             my $packager = new Software::Packager('aix');
9              
10             =head1 DESCRIPTION
11              
12             This module is used to create software packages in a Backup-format file (bff)
13             suitable for installation with installp.
14              
15             This module creates packages for AIX 4.1 and higher only.
16             Due to the compatability requirements of Software::Packager multiple
17             components in the same package are not supported. This may be changed at some
18             point in the future.
19              
20             This module is in part a baised on the workings of the lppbuild scripts. Where
21             possible I've worked from the standards, where I had no idea what they were
22             talking about I refered to the lppbuild scripts for an understanding. As such
23             I'd like to thank the writers of lppbuild version 2.1.
24             I believe these scripts to be written by Jim Abbey. Who ever it was thanks
25             for your work. It has proven envaluable.
26             lppbuild is available from http://aixpdslib.seas.ucla.edu/
27              
28             Please note that this module will eventually comply with the IBM documented
29             standard which can be found at
30              
31             http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/genprogc/pkging_sw4_install.htm
32              
33             =head1 FUNCTIONS
34              
35             =cut
36              
37             package Software::Packager::Aix;
38              
39             ####################
40             # Standard Modules
41 1     1   1662 use strict;
  1         2  
  1         44  
42 1     1   5 use File::Path;
  1         1  
  1         66  
43 1     1   752 use File::Copy;
  1         2428  
  1         51  
44 1     1   6 use File::Basename;
  1         1  
  1         62  
45 1     1   5 use Cwd;
  1         1  
  1         51  
46             # Custom modules
47 1     1   4 use Software::Packager;
  1         2  
  1         17  
48 1     1   644 use Software::Packager::Object::Aix;
  1         4  
  1         27  
49              
50             ####################
51             # Variables
52 1     1   5 use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
  1         1  
  1         5416  
53             @ISA = qw( Software::Packager );
54             @EXPORT = qw();
55             @EXPORT_OK = qw();
56             $VERSION = 0.10;
57              
58             ####################
59             # Functions
60              
61             ################################################################################
62             # Function: new()
63             # Description: This function creates and returns a new Packager object.
64             # Arguments: none.
65             # Return: new Packager object.
66             #
67             sub new
68             {
69 1     1 1 9 my $class = shift;
70 1         4 my $self = bless {}, $class;
71              
72 1         12 return $self;
73             }
74              
75             ################################################################################
76             # Function: add_item()
77              
78             =head2 B
79              
80             The method overrides the add_item method of Software::Packager to use
81             Software::Packager::Object::Aix.
82             For more details see the documentation in:
83              
84             Software::Packager
85             Software::Packager::Object
86             Software::Packager::Object::Aix
87              
88             =cut
89             sub add_item
90             {
91 29     29 1 736 my $self = shift;
92 29         94 my %data = @_;
93            
94             # Hardlinks are associated with the file they refernce on AIX
95 29 100       88 if ($data{'TYPE'} =~ /hardlink/i)
96             {
97 1 50       5 unless (exists $self->{'OBJECTS'}->{$data{'SOURCE'}})
98             {
99 0         0 warn "Error: Cannot add a hard link for $data{'DESTINATION'}\n";
100 0         0 warn " until the source object has been added to the package.\n";
101 0         0 return undef;
102             }
103 1         5 $self->{'OBJECTS'}->{$data{'SOURCE'}}->links($data{'DESTINATION'});
104 1         3 return 1;
105             }
106              
107 28         208 my $object = new Software::Packager::Object::Aix(%data);
108              
109 28 100       531 return undef unless $object;
110              
111             # check that the object has a unique destination
112 26 50       74 return undef if $self->{'OBJECTS'}->{$object->destination()};
113              
114 26         66 $self->{'OBJECTS'}->{$object->destination()} = $object;
115             }
116              
117             ################################################################################
118             # Function: lpp_package_type()
119              
120             =head2 B
121              
122             This method sets or returns the lpp package type.
123             The lpp package types are
124             "I" for an install package
125             "ML" for a maintenance level package
126             "S" for a single update package
127              
128             If the lpp package type is not set, the default of "I" for an install package is
129             set (version minor and fix numbers are 0) and "S" for an update package
130             (version minor and/or fix numbers are non 0)
131              
132             =cut
133             sub lpp_package_type
134             {
135 1     1 1 7 my $self = shift;
136 1         8 my $value = shift;
137 1 50       10 if ($value)
138             {
139 0         0 $self->{'LPP_PACKAGE_TYPE'} = $value;
140             }
141             else
142             {
143 1 50       16 if ($self->{'LPP_PACKAGE_TYPE'})
144             {
145 0         0 return $self->{'LPP_PACKAGE_TYPE'};
146             }
147             else
148             {
149 1 50       9 if ($self->_lppmode() eq 'I')
150             {
151 1         9 return 'I';
152             }
153             else
154             {
155 0         0 return 'S';
156             }
157             }
158             }
159             }
160              
161             ################################################################################
162             # Function: component_name()
163              
164             =head2 B
165              
166             $packager->component_name($value);
167             $component_name = $packager->component_name();
168              
169             This method sets or returns the component name for this package.
170             The compoment name is a required value for AIX packages.
171              
172             =cut
173             sub component_name
174             {
175 5     5 1 253 my $self = shift;
176 5         8 my $value = shift;
177 5 100       11 if ($value)
178             {
179 1         3 $self->{'PACKAGE_COMPONENT'} = $value;
180             }
181             else
182             {
183 4         12 return $self->{'PACKAGE_COMPONENT'};
184             }
185             }
186              
187             ################################################################################
188             # Function: package()
189              
190             =head2 B
191              
192             $packager->package();
193              
194             This method overrides the base API in Software::Packager.
195             it does all the nasty work of creating the package.
196              
197             =cut
198             sub package
199             {
200 1     1 1 2 my $self = shift;
201              
202             # Do some checks before we build.
203 1 50       3 unless (scalar $self->program_name())
204             {
205 0         0 warn "Error: This package doesn't have the program name set. This is required.";
206 0         0 return undef;
207             }
208 1 50       8 unless (scalar $self->component_name())
209             {
210 0         0 warn "Error: This package doesn't have the component name set. This is required.";
211 0         0 return undef;
212             }
213              
214 1 50       3 unless ($self->_setup())
215             {
216 0         0 warn "Error: Problems were encountered in the setup phase\n";
217 0         0 return undef;
218             }
219              
220             # Now create the final backup file format package.
221 1 50       20 unless ($self->_create_bff())
222             {
223 1         18 warn "Error: Problems were encountered creating the backup format file: $!\n";
224 1         50 return undef;
225             }
226            
227 0 0       0 unless ($self->_cleanup())
228             {
229 0         0 warn "Error: Problems were encountered in the cleanup phase\n";
230 0         0 return undef;
231             }
232 0         0 return 1;
233             }
234              
235             ################################################################################
236             # Function: _setup()
237             # Description: This method sets up the temporary build structure.
238             # Arguments: None
239             # Returns: True is all goes okay else undef
240             #
241             sub _setup
242             {
243 1     1   2 my $self = shift;
244 1         2 my $tmp_dir = $self->tmp_dir();
245              
246 1 50       13 unless (-d $tmp_dir)
247             {
248 1 50       282 unless (mkpath($tmp_dir, 0, 0750))
249             {
250 0         0 warn "Error: Problems were encountered creating directory \"$tmp_dir\": $!\n";
251 0         0 return undef;
252             }
253             }
254              
255             # determine if we have a root part. If so set up some new objects and
256             # modify the objects that are not installed in /usr
257 1 50       19 if ($self->_find_lpp_type() eq "B")
    0          
    0          
258             {
259 1         5 $self->_setup_for_root();
260             }
261             elsif ($self->_find_lpp_type() eq "U")
262             {
263             # This object is required for user parts
264 0         0 my %data;
265 0         0 $data{'TYPE'} = 'directory';
266 0         0 $data{'MODE'} = '0755';
267 0         0 $data{'DESTINATION'} = "/usr/lpp/" . $self->program_name();
268 0 0       0 unless ($self->add_item(%data))
269             {
270 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
271             }
272             }
273             elsif ($self->_find_lpp_type() eq "H")
274             {
275             # This object is required for share parts
276 0         0 my %data;
277 0         0 $data{'TYPE'} = 'directory';
278 0         0 $data{'MODE'} = '0755';
279 0         0 $data{'DESTINATION'} = "/usr/share/lpp/" . $self->program_name();
280 0 0       0 unless ($self->add_item(%data))
281             {
282 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
283             }
284             }
285              
286             # Create the controls files and add them to the package.
287 1 50       7 unless ($self->_create_control_files())
288             {
289 0         0 warn "Error: Problems were encountered creating the control files for the package: $!\n";
290 0         0 return undef;
291             }
292              
293             # create the package structure under the tmp_dir
294 1 50       50 unless ($self->_create_package_structure())
295             {
296 0         0 warn "Error: Problems were encountered creating the package structure: $!\n";
297 0         0 return undef;
298             }
299              
300             # create the lpp_name file
301 1 50       33 unless ($self->_create_lpp_name())
302             {
303 0         0 warn "Error: Problems were encountered creating the file lpp_name: $!\n";
304 0         0 return undef;
305             }
306              
307 1         23 return 1;
308             }
309              
310             ################################################################################
311             # Function: _cleanup()
312             # Description: This method cleans up after us.
313             # Arguments: None
314             # Returns: True is all goes okay else undef
315             #
316             sub _cleanup
317             {
318 0     0   0 my $self = shift;
319 0         0 my $tmp_dir = $self->tmp_dir();
320              
321             # there has to be a better way to to this!
322 0         0 system("chmod -R 0777 $tmp_dir 2>/dev/null");
323             #rmtree($tmp_dir, 0, 1);
324              
325 0         0 return 1;
326             }
327              
328             ################################################################################
329             # Function: version()
330              
331             =head2 B
332              
333             This method is used to set the version and return it in the correct format
334             required for AIX.
335              
336             Any invalid entries in the version will be automatically corrected and a
337             warning printed.
338              
339             This is a excerpt from the standard.
340              
341             The fileset level is referred to as the level or alternatively as the v.r.m.f or VRMF and has the form:
342              
343             Version.Release.ModificationLevel.FixLevel[.FixID]
344              
345             Version A numeric field of 1 to 2 digits that identifies the version number.
346             Release A numeric field of 1 to 2 digits that identifies the release number.
347             ModificationLevel A numeric field of 1 to 4 digits that identifies the modification level.
348             FixLevel A numeric field of 1 to 4 digits that identifies the fix level.
349             FixID A character field of 1 to 9 characters identifying the fix identifier.
350             The FixID is used by Version 3.2-formatted fileset updates only.
351              
352             A base fileset installation level is the full initial installation level of a fileset.
353             This level contains all files in the fileset, as opposed to a fileset update,
354             which may contain a subset of files from the full fileset.
355              
356             All filesets in a software package should have the same fileset level,
357             though it is not required for AIX Version 4.1-formatted packages.
358              
359             For all new levels of a fileset, the fileset level must increase.
360             The installp command uses the fileset level to check for a later level of the
361             product on subsequent installations.
362              
363             Fileset level precedence reads from left to right (for example, 3.2.0.0 is a
364             newer level than 2.3.0.0).
365              
366              
367             Fileset Level Rules and Conventions for AIX Version 4.1-Formatted Filesets
368             The following conventions and rules have been put in place in order to simplify
369             the software maintenance for product developers and customers:
370              
371             A base fileset installation level should have a fix level of 0 (zero).
372              
373             A base fileset installation level package must contain the functionality
374             provided in other installation packages for that fileset with lower fileset
375             levels. For example, the Plan.Day level 2.1 fileset must contain the
376             functionality provided in the Plan.Day level 1.1 fileset.
377              
378             A fileset update must have either a non-zero modification level or a non-zero
379             fix level.
380              
381             A fileset update must have the same version and release numbers as the base
382             fileset installation level to which it is to be applied.
383              
384             Unless otherwise specified in the software package, a fileset update with a
385             non-zero fix level must be an update to the fileset with the same version
386             number, release number, and modification level and a zero fix level. Providing
387             information in the requisite section of the lpp_name file causes an exception to
388             this rule.
389              
390             Unless otherwise specified in the software package, a fileset update with a
391             non-zero modification level and a zero fix level must be an update to the
392             fileset with the same version number and release number and a zero modification
393             level. Providing information in the requisite section of the lpp_name file
394             causes an exception to this rule.
395              
396             A fileset update must contain the functionality of the fileset's previous
397             updates that apply to the same fileset level.
398              
399             =cut
400             sub version
401             {
402 11     11 1 170 my $self = shift;
403 11         18 my $version = shift;
404 11 100       29 if (scalar $version)
405             {
406 5 100       33 if ($version !~ /^\d+(\.\d+){3,4}$/)
407             {
408 1         7 warn "Warning: The version \"$version\" is not a 4 or 5 field Dewey-Decimal number. It will be modified.\n";
409 1         3 $version =~ tr/0-9\.//cd;
410             }
411 5         20 my ($major, $release, $mod, $fix, $fixid) = split /\./, $version;
412             # check that we have 4 parts if not then create them
413 5 100       14 $major = 0 unless $major;
414 5 100       11 $release = 0 unless $release;
415 5 100       623 $mod = 0 unless $mod;
416 5 100       8 $fix = 0 unless $fix;
417              
418             # check that the major and release values are non zero.
419 5 100       12 $major = 1 if $major <= 0;
420 5 100       10 $release = 1 if $release <= 0;
421              
422             # Check that the version fields are the correct length.
423 5 100       13 if (length $major > 2)
424             {
425 1         8 warn "Warning: The \"Version\" field of the version contains more than two charaters.\n";
426 1         5 warn " It will be truncated.\n";
427 1         6 $major = sprintf("%.2s", $major);
428             }
429 5 100       8 if (length $release > 2)
430             {
431 1         5 warn "Warning: The \"Release\" field of the version contains more than two charaters.\n";
432 1         7 warn " It will be truncated.\n";
433 1         3 $release = sprintf("%.2s", $release);
434             }
435 5 100       10 if (length $mod > 4)
436             {
437 1         6 warn "Warning: The \"ModificationLevel\" field of the version contains more than four charaters.\n";
438 1         6 warn " It will be truncated.\n";
439 1         3 $mod = sprintf("%.4s", $mod);
440             }
441 5 100       11 if (length $fix > 4)
442             {
443 1         6 warn "Warning: The \"FixLevel\" field of the version contains more than four charaters.\n";
444 1         7 warn " It will be truncated.\n";
445 1         3 $fix = sprintf("%.4s", $fix);
446             }
447 5 100 100     17 if ((defined $fixid) and (length $fixid > 9))
448             {
449 1         5 warn "Warning: The \"FixID\" field of the version contains more than nine charaters.\n";
450 1         4 warn " It will be truncated.\n";
451 1         5 $fixid = sprintf("%.9s", $fixid);
452             }
453              
454             # set the lppmode
455 5 100 66     20 if (($mod eq 0) and ($fix eq 0))
456             {
457 2         6 $self->_lppmode('I');
458             }
459             else
460             {
461 3         8 $self->_lppmode('U');
462             }
463              
464 5         14 $self->{'PACKAGE_VERSION'} = "$major.$release.$mod.$fix";
465 5 100       12 $self->{'PACKAGE_VERSION'} .= ".$fixid" if defined $fixid;
466             }
467              
468 11         34 return $self->{'PACKAGE_VERSION'};
469             }
470              
471             ################################################################################
472             # Function: _find_lpp_type()
473             # Description: This method finds the type of LPP we are building.
474             # If all components are under /usr/share then the part is a SHARE package.
475             # If all components are under /usr then the part is a USER package.
476             # If components are under any other directory then the part is a ROOT+USER
477             # package.
478             # ROOT only parts are not permitted.
479             # SHARE + ROOT and or USER parts are not permitted.
480             # Returns: The LPP code for the part type on success and undef if there are
481             # errors.
482             # A USER part will return U.
483             # A ROOT+USER part will return B
484             # A SHARE part will return H
485             # Arguments: None
486             #
487             sub _find_lpp_type
488             {
489 7     7   36 my $self = shift;
490 7         12 my $share = 0;
491 7         8 my $user = 0;
492 7         10 my $root = 0;
493              
494             # As this function may be slow to run only run it once.
495 7 100       54 return $self->{'LPP_TYPE'} if scalar $self->{'LPP_TYPE'};
496              
497 1         10 foreach my $object ($self->get_object_list())
498             {
499 20 50       57 if ($object->lpp_type_is_share()){ $share++; next;};
  0         0  
  0         0  
500 20 100       43 if ($object->lpp_type_is_user()){ $user++; next;};
  18         18  
  18         22  
501 2 50       6 if ($object->lpp_type_is_root()){ $root++; next;};
  2         3  
  2         3  
502             }
503              
504 1 50 33     12 if ($share and $user)
    50 33        
    50          
    0          
    0          
505             {
506 0         0 warn "Error: Packages with SHARE and USER parts are not permitted.\n";
507 0         0 return undef;
508             }
509             elsif ($share and $root)
510             {
511 0         0 warn "Error: Packages with SHARE and ROOT parts are not permitted.\n";
512 0         0 return undef;
513             }
514             elsif ($root)
515             {
516 1         6 $self->{'LPP_TYPE'} = 'B';
517             }
518             elsif ($user)
519             {
520 0         0 $self->{'LPP_TYPE'} = 'U';
521             }
522             elsif ($share)
523             {
524 0         0 $self->{'LPP_TYPE'} = 'H';
525             }
526             else
527             {
528 0         0 warn "Error: Package type could not be determined.\n";
529 0         0 return undef;
530             }
531             }
532              
533             ################################################################################
534             # Function: _lppmode()
535             # Description: This method sets or returns the lppmode.
536             # The lppmode can be either install (I) or update (U).
537             # This is set when the version is set.
538             # Argument: The mode of the package.
539             # Returns: The mode of the package if nothing is passed.
540             #
541             sub _lppmode
542             {
543 11     11   24 my $self = shift;
544 11         15 my $value = shift;
545 11 100       29 if ($value)
546             {
547 5         17 $self->{'LPPMODE'} = $value;
548             }
549             else
550             {
551 6         37 return $self->{'LPPMODE'};
552             }
553             }
554              
555             ################################################################################
556             # Function: _create_lpp_name()
557             # Description: This method creates the file lpp_name for the package.
558             # Argument: None.
559             # Returns: None.
560             #
561             sub _create_lpp_name
562             {
563 1     1   13 my $self = shift;
564 1         46 my $lpp_name_file = $self->tmp_dir() . "/lpp_name";
565 1         154 open (LPPNAME, ">$lpp_name_file");
566              
567 1         20 print LPPNAME "4 R";
568 1         19 print LPPNAME " " . $self->lpp_package_type();
569 1         101 print LPPNAME " " . $self->program_name();
570 1         21 print LPPNAME " {\n";
571              
572 1         9 print LPPNAME " " . $self->program_name() .".". $self->component_name();
573 1         11 print LPPNAME " " . $self->version();
574              
575             # not sure what this is for. I'll have to check the specs.
576 1         5 print LPPNAME " 1";
577              
578 1 50       21 if ($self->reboot_required())
579             {
580 0         0 print LPPNAME " b";
581             }
582             else
583             {
584 1         13 print LPPNAME " N";
585             }
586 1         14 print LPPNAME " " . $self->_find_lpp_type();
587              
588 1         11 print LPPNAME " en_US";
589 1         13 print LPPNAME " ". $self->description() . "\n";
590 1         17 print LPPNAME "[\n";
591              
592 1 50       16 if ($self->prerequisites())
593             {
594             # TODO: This needs to be implemented.
595             }
596 1         18 print LPPNAME "\%\n";
597 1         7 print LPPNAME $self->_find_disk_usage();
598              
599             # TODO: need to implement page space.
600             # TODO: need to implement install space. (space required to extract crontrol files from liblpp.a
601             # TODO: need to implement save space.
602              
603 1         10 print LPPNAME "\%\n";
604            
605             # TODO: need to implement supersede ability
606              
607 1         6 print LPPNAME "\%\n";
608              
609             # TODO: need to implement fix information
610              
611 1         3 print LPPNAME "]\n";
612 1         5 print LPPNAME "}\n";
613 1         66 close LPPNAME;
614             }
615              
616             ################################################################################
617             # Function: _find_disk_usage()
618             # Description: This method finds the disk usage for the package directories.
619             # Arguments: None.
620             # Returns: The disk usage.
621             #
622             sub _find_disk_usage
623             {
624 1     1   3 my $self = shift;
625 1         138 my $dir = $self->tmp_dir();
626 1         17 my $cwd = getcwd();
627 1         18 chdir $dir;
628            
629             # find the directories
630 1         64597 my @directories = `find . ! -type d -exec dirname {} \\; | sort -u`;
631              
632             # find the disk usage
633 1         33 my $usage;
634 1         24 foreach my $dir (@directories)
635             {
636 8         147 chomp $dir;
637 8 100       76 $dir = "./" if $dir eq ".";
638 8         121302 $usage .= `du -s $dir |awk '{print substr(\$2,2) " " \$1}'`;
639             }
640              
641 1         48 chdir $cwd;
642 1         35 return $usage;
643             }
644              
645             ################################################################################
646             # Function: _create_package_structure()
647             # Description: This method creates the package structure for the package under
648             # the tmp directory.
649             # Arguments: None.
650             # Returns: None.
651             #
652             sub _create_package_structure
653             {
654 1     1   6 my $self = shift;
655 1         17 my $tmp_dir = $self->tmp_dir();
656              
657 1         18 my $lpp_type = $self->_find_lpp_type();
658 1         18 foreach my $object ($self->get_object_list())
659             {
660 26         1918 my $destination = "$tmp_dir". $object->destination();
661 26         542 my $source = $object->source();
662 26         890 my $type = $object->type();
663 26         418 my $mode = $object->mode();
664 26         1826 my $user = $object->user();
665 26         367 my $group = $object->group();
666              
667 26 100       686 if ($type =~ /directory/i)
    100          
    50          
    50          
668             {
669 10 50       1076 unless (-d $destination)
670             {
671 10         21415 mkpath($destination, 0, oct($mode));
672             }
673 10 50       94669 unless (system("chown $user $destination") eq 0)
674             {
675 0         0 warn "Error: Couldn't set the user to \"$user\" for \"$destination\": $!\n";
676 0         0 return undef;
677             }
678 10 50       91187 unless (system("chgrp $group $destination") eq 0)
679             {
680 0         0 warn "Error: Couldn't set the group to \"$group\" for \"$destination\": $!\n";
681 0         0 return undef;
682             }
683             }
684             elsif ($type =~ /file/i)
685             {
686 15         2502 my $directory = dirname($destination);
687 15 100       781 unless (-d $directory)
688             {
689 1         808 mkpath($directory, 0, 0755);
690             }
691 15 50       260 unless (copy($source, $destination))
692             {
693 0         0 warn "Error: Couldn't copy $source to $destination: $!\n";
694             }
695 15 50       147746 unless (system("chown $user $destination") eq 0)
696             {
697 0         0 warn "Error: Couldn't set the user to \"$user\" for \"$destination\": $!\n";
698 0         0 return undef;
699             }
700 15 50       120945 unless (system("chgrp $group $destination") eq 0)
701             {
702 0         0 warn "Error: Couldn't set the group to \"$group\" for \"$destination\": $!\n";
703 0         0 return undef;
704             }
705 15 50       119497 unless (system("chmod $mode $destination") eq 0)
706             {
707 0         0 warn "Error: Couldn't set the mode to \"$mode\" for \"$destination\": $!\n";
708 0         0 return undef;
709             }
710             }
711             elsif ($type =~ /hard/i)
712             {
713 0 0       0 unless (link $source, $destination)
714             {
715 0         0 warn "Error: Could not create hard link from $source to $destination:\n$!\n";
716 0         0 return undef;
717             }
718             }
719             elsif ($type =~ /soft/i)
720             {
721 1 50       104 unless (symlink $source, $destination)
722             {
723 0         0 warn "Error: Could not create soft link from $source to $destination:\n$!\n";
724 0         0 return undef;
725             }
726             }
727             else
728             {
729 0         0 warn "Warning: Don't know what type of object \"$destination\" is.\n";
730             }
731             }
732              
733             # Now we need to remove the user_liblpp.a and root_liblpp.a so that they
734             # are not added to the space requirements in the file lpp_name
735 1         803 unlink "$tmp_dir/user_liblpp.a";
736 1 50       137 unlink "$tmp_dir/root_liblpp.a" if -f "$tmp_dir/root_liblpp.a";
737              
738 1         45 return 1;
739             }
740              
741             ################################################################################
742             # Function: _create_control_files()
743             # Description: This method creates the lpp control files (liblpp.a). as well as
744             # creating the apply list and inventory which are essentially
745             # required files.
746             # check what sort of install we have.
747             # A share install will only have one liblpp.a in
748             # /usr/share/lpp/PROGRAM/liblpp.a
749             # A user install will only have one liblpp.a in
750             # /usr/lpp/PROGRAM/liblpp.a
751             # A root install will have two liblpp.a files in
752             # /usr/lpp/PROGRAM/liblpp.a and
753             # /usr/lpp/PROGRAM../inst_root/liblpp.a
754             # Arguments: None.
755             # Returns: true on success else undef.
756             #
757             sub _create_control_files
758             {
759 1     1   3 my $self = shift;
760 1         6 my $tmp_dir = $self->tmp_dir();
761              
762 1         11 my $program_name = $self->program_name();
763 1         10 my $component_name = $self->component_name();
764 1         5 my $version = $self->version();
765              
766 1         2 my $liblpp_dir = "/usr";
767 1 50       3 $liblpp_dir .= "/share/lpp" if $self->_find_lpp_type() eq 'H';
768 1 50       3 $liblpp_dir .= "/lpp" if $self->_find_lpp_type() =~ /U|B/;
769 1         3 $liblpp_dir .= "/$program_name";
770 1 50       4 if ($self->_lppmode() eq "U")
771             {
772 0         0 $liblpp_dir .= "/$program_name";
773 0         0 $liblpp_dir .= ".$component_name";
774 0         0 $liblpp_dir .= "/$version";
775             }
776 1         3 my $liblpp_file = "$liblpp_dir/liblpp.a";
777 1 50       13 my $root_liblpp_dir = "$liblpp_dir/inst_root" if $self->_find_lpp_type() =~ /B/;
778 1         3 my $root_liblpp_file .= "$root_liblpp_dir/liblpp.a";
779            
780             # first create the ROOT liblpp.a file so it can be added to the USER
781             # part if there is a ROOT part.
782 1         3 my $applylist = "$program_name.$component_name.al";
783 1         2 my $inventory = "$program_name.$component_name.inventory";
784              
785 1         2 my $control_dir = "$tmp_dir/control_files";
786 1 50       49 unless (-d $control_dir)
787             {
788 1         242 mkpath($control_dir, 0, 0755);
789             }
790              
791 1 50       5 if ($self->_find_lpp_type() eq "B")
792             {
793 1         107 open (AL, ">>$control_dir/$applylist");
794 1         110 open (INV, ">>$control_dir/$inventory");
795 1         18 foreach my $object ($self->get_directory_objects(), $self->get_file_objects(), $self->get_link_objects())
796             {
797 22         200 my $destination = $object->destination();
798 22         77 my $source = $object->source();
799 22         152 my $owner = $object->user();
800 22         92 my $group = $object->group();
801 22         81 my $type = $object->type();
802 22         207 my $mode = $object->mode();
803 22         289 my $inv_type = $object->inventory_type();
804            
805 22 100       218 next unless $destination =~ m#/inst_root/#;
806            
807 2         31 $destination =~ s#^$root_liblpp_dir##;
808            
809             # This is all that needs to be done for the apply list
810 2 50       24 print AL ".$destination\n" unless $inv_type eq 'SYMLINK';
811            
812 2         14 print INV "$destination:\n";
813 2         5 print INV "\tclass = apply,inventory,$program_name.$component_name\n";
814 2         5 print INV "\towner = $owner\n";
815 2         5 print INV "\tgroup = $group\n";
816 2         8 print INV "\tmode = $mode\n";
817 2         3 print INV "\ttype = $inv_type\n";
818 2 100       14 if ($inv_type =~ /FILE/)
819             {
820 1 50       4 if ($type =~ /config|volatile/i)
821             {
822 0         0 print INV "\tsize = VOLATILE\n";
823 0         0 print INV "\tchecksum = VOLATILE\n";
824             }
825             else
826             {
827 1         29 my @stats = stat($source);
828 1         4 print INV "\tsize = $stats[7]\n";
829            
830 1         6609 my $checksum = `sum $source`;
831 1         31 chomp $checksum;
832 1         36 $checksum =~ s/(\d+\s+\d+\s).*/$1/;
833 1         40 print INV "\tchecksum = \"$checksum\"\n";
834             }
835             }
836 2         49 my $links = $object->links();
837 2 50       7 if (scalar $links)
838             {
839 0         0 print INV "\tlinks = $links\n";
840             }
841 2 50       9 if ($inv_type eq 'SYMLINK')
842             {
843 0         0 print INV "\ttarget = $source\n";
844             }
845 2         30 print INV "\n";
846             }
847 1         44 close AL;
848 1         65 close INV;
849             }
850              
851             # now archive all the control files
852             # We need to do the root part first.
853 1 50       73 opendir (DIR, "$control_dir") or die "Error: Cannot open temporary directory \"$control_dir\" for reading: $!\n";
854 1         63 my @control_file_list = readdir DIR;
855 1         21 closedir DIR;
856 1         9 foreach my $file (@control_file_list)
857             {
858 4 100       106 next if $file =~ /^.$|^..$/;
859 2 50       22172 unless (system("ar -c -q $tmp_dir/root_liblpp.a $control_dir/$file") == 0)
860             {
861 0         0 warn "Warning: There were problems adding the control file $file to $tmp_dir/root_liblpp.a:\n$!";
862 0         0 return undef;
863             }
864             }
865 1         887 rmtree($control_dir, 0, 1);
866              
867 1 50       21 if (-f "$tmp_dir/root_liblpp.a")
868             {
869             # Add the root liblpp.a to the package if it exists
870 1         6 my %data;
871 1         10 $data{'TYPE'} = 'file';
872 1         18 $data{'MODE'} = '0755';
873 1         11 $data{'SOURCE'} = "$tmp_dir/root_liblpp.a";
874 1         7 $data{'DESTINATION'} .= "$root_liblpp_file";
875 1 50       20 unless ($self->add_item(%data))
876             {
877 0         0 warn "Error: Couldn't add $tmp_dir/root_liblpp.a to the package\n";
878 0         0 return undef;
879             }
880             }
881              
882             # Now we need to add any directories for the root part that don't exist.
883             # as they are required to be deployed in the USER part. seems weird to
884             # me but that's how it is. (There is logic to madness sometimes though
885             # hard to see.)
886 1         18 $self->_add_objects_for_user_part();
887              
888             # Now create the USER or SHARE control files.
889 1 50       50 unless (-d $control_dir)
890             {
891 1         195 mkpath($control_dir, 0, 0755);
892             }
893              
894 1         109 open (AL, ">>$control_dir/$applylist");
895 1         76 open (INV, ">>$control_dir/$inventory");
896 1         10 foreach my $object ($self->get_directory_objects(), $self->get_file_objects(), $self->get_link_objects())
897             {
898 25         365 my $destination = $object->destination();
899 25         242 my $source = $object->source();
900 25         241 my $owner = $object->user();
901 25         119 my $group = $object->group();
902 25         122 my $type = $object->type();
903 25         280 my $mode = $object->mode();
904 25         755 my $inv_type = $object->inventory_type();
905              
906             # This is all that needs to be done for the apply list
907 25         410 print AL ".$destination\n";
908             # I'm not sure if we should be doing this here to. some more
909             # testing is required to check this as the standard is not to
910             # clear.
911             #print AL ".$destination\n" unless $inv_type eq 'SYMLINK';
912              
913             # if there is a root part we don't need to set the inventory
914             # data for it in the user part
915 25 100       129 next if $destination =~ m#/usr/lpp#;
916              
917 18         50 print INV "$destination:\n";
918 18         93 print INV "\tclass = apply,inventory,$program_name.$component_name\n";
919 18         56 print INV "\towner = $owner\n";
920 18         34 print INV "\tgroup = $group\n";
921 18         40 print INV "\tmode = $mode\n";
922 18         35 print INV "\ttype = $inv_type\n";
923 18 100       88 if ($inv_type =~ /FILE/)
924             {
925 12 50       34 if ($type =~ /config|volatile/i)
926             {
927 0         0 print INV "\tsize = VOLATILE\n";
928 0         0 print INV "\tchecksum = VOLATILE\n";
929             }
930             else
931             {
932 12         488 my @stats = stat($source);
933 12         53 print INV "\tsize = $stats[7]\n";
934              
935 12         80272 my $checksum = `sum $source`;
936 12         388 chomp $checksum;
937 12         305 $checksum =~ s/(\d+\s+\d+\s).*/$1/;
938 12         355 print INV "\tchecksum = \"$checksum\"\n";
939             }
940             }
941 18         643 my $links = $object->links();
942 18 100       65 if (scalar $links)
943             {
944 1         9 print INV "\tlinks = $links\n";
945             }
946 18 100       54 if ($inv_type eq 'SYMLINK')
947             {
948 1         16 print INV "\ttarget = $source\n";
949             }
950 18         342 print INV "\n";
951             }
952 1         84 close AL;
953 1         32 close INV;
954              
955             # This is a list of possible config files that can be added to the liblpp.a archive.
956             # TODO: need to make a method to set all of these files.
957             #my @config_files = qw( cfginfo cfgfiles err fixdata namelist odmadd rm_inv trc config config_u odmdel pre_d pre_i pre_u pre_rm posti post_u unconfig unconfig_u unodmadd unport_i unpost_u unpre_i unpre_u copyright );
958              
959             # The copyright file is mandatory so create it if it not set
960 1 50       122 if ($self->license_file())
961             {
962 0 0       0 return undef unless copy($self->license_file(), "$control_dir/$program_name.$component_name.copyright");
963             }
964             else
965             {
966 1         138 open(FILE, ">$control_dir/lpp.copyright");
967 1         13 print FILE "No specific copyright in effect.\n";
968 1         50 close FILE;
969             }
970              
971             # this will print a message for the user that a reboot is required.
972 1 50       16 if ($self->reboot_required())
973             {
974 0         0 open(FILE, ">$control_dir/$program_name.$component_name.cfginfo");
975 0         0 print FILE "BOOT\n";
976 0         0 close FILE;
977             }
978              
979             # now archive all the control files
980             # We need to do the root part first.
981 1 50       46 opendir (DIR, "$control_dir") or die "Error: Cannot open temporary directory \"$control_dir\" for reading: $!\n";
982 1         41 @control_file_list = readdir DIR;
983 1         26 closedir DIR;
984 1         10 foreach my $file (@control_file_list)
985             {
986 5 100       273 next if $file =~ /^.$|^..$/;
987 3 50       27741 unless (system("ar -c -q $tmp_dir/user_liblpp.a $control_dir/$file") == 0)
988             {
989 0         0 warn "Warning: There were problems adding the control file $file to $tmp_dir/user_liblpp.a:\n$!";
990 0         0 return undef;
991             }
992             }
993 1         2083 rmtree($control_dir, 0, 1);
994              
995 1 50       37 if (-f "$tmp_dir/user_liblpp.a")
996             {
997             # Add the root liblpp.a to the package if it exists
998 1         9 my %data;
999 1         11 $data{'TYPE'} = 'file';
1000 1         10 $data{'MODE'} = '0755';
1001 1         10 $data{'SOURCE'} = "$tmp_dir/user_liblpp.a";
1002 1         9 $data{'DESTINATION'} .= "$liblpp_file";
1003 1 50       23 unless ($self->add_item(%data))
1004             {
1005 0         0 warn "Error: Couldn't add $tmp_dir/user_liblpp.a to the package\n";
1006 0         0 return undef;
1007             }
1008             }
1009              
1010 1         30 return 1;
1011             }
1012              
1013             ################################################################################
1014             # Function: _create_bff()
1015             # Description: This finction creates the backup format file that is the actual
1016             # package.
1017             # Arguments: None.
1018             # Returns: True on success else undef.
1019             #
1020             sub _create_bff
1021             {
1022 1     1   9 my $self = shift;
1023 1         25 my $tmp_dir = $self->tmp_dir();
1024              
1025 1         26 my $cwd = getcwd();
1026 1         13 chdir $tmp_dir;
1027              
1028 1         9 my @files_to_backup = ('./lpp_name');
1029 1         18 foreach my $object ($self->get_directory_objects(), $self->get_file_objects(), $self->get_link_objects())
1030             {
1031 26         240 push @files_to_backup, ".".$object->destination();
1032             }
1033              
1034 1 50 0     177 open (FILE, ">./backup.list") or
      0        
1035             warn "Error: Cannot open $tmp_dir/backup.list for writing: $!\n" and
1036             chdir $cwd and
1037             return undef;
1038              
1039 1         4 foreach my $file (@files_to_backup)
1040             {
1041 27         64 print FILE "$file\n";
1042             }
1043 1         56 close FILE;
1044              
1045 1         10 my $package_file = $self->output_dir();
1046 1         20 $package_file .= "/" . $self->package_name();
1047 1         17 $package_file .= ".bff";
1048 1 50       5182 unless (system("backup -vi -q -f $package_file < ./backup.list") eq 0)
1049             {
1050 1         93 warn "Error: Failed to create the Backup-format file. $!\n";
1051 1         29 chdir $cwd;
1052 1         66 return undef;
1053             }
1054            
1055 0         0 chdir $cwd;
1056              
1057 0         0 return 1;
1058             }
1059              
1060             ################################################################################
1061             # Function: _setup_for_root()
1062             # Description: This function creates a bunch of objects that need to be added
1063             # for the root portion of the package and modifies objects that
1064             # are not installed in /usr.
1065             # Arguments: None.
1066             # Returns: None but modifies the objects.
1067             #
1068             sub _setup_for_root
1069             {
1070 1     1   2 my $self = shift;
1071              
1072             # create objects for the root portion of the package
1073 1         1 my %data;
1074 1         3 $data{'TYPE'} = 'directory';
1075 1         2 $data{'MODE'} = '0755';
1076 1         5 $data{'DESTINATION'} = "/usr/lpp/" . $self->program_name();
1077 1 50       13 unless ($self->add_item(%data))
1078             {
1079 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
1080             }
1081              
1082 1 50       4 if ($self->_lppmode() eq "U")
1083             {
1084 0         0 $data{'DESTINATION'} .= "/" . $self->program_name();
1085 0         0 $data{'DESTINATION'} .= "." . $self->component_name();
1086 0 0       0 unless ($self->add_item(%data))
1087             {
1088 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
1089             }
1090 0         0 $data{'DESTINATION'} .= "/" . $self->version();
1091 0 0       0 unless ($self->add_item(%data))
1092             {
1093 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
1094             }
1095             }
1096              
1097 1         4 $data{'DESTINATION'} .= "/inst_root";
1098 1 50       70 unless ($self->add_item(%data))
1099             {
1100 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
1101             }
1102              
1103             # modify all objects not installed under /usr
1104 1         5 foreach my $object ($self->get_object_list())
1105             {
1106 22         67 my $destination = $object->destination();
1107 22 100       82 next if $destination =~ m#^/usr#;
1108 2         21 my $new_destination = "/usr/lpp";
1109 2         9 $new_destination .= "/" . $self->program_name();
1110 2 50       23 if ($self->_lppmode() eq "U")
1111             {
1112 0         0 $new_destination .= "/" . $self->program_name();
1113 0         0 $new_destination .= "." . $self->component_name();
1114 0         0 $new_destination .= "/" . $self->version();
1115             }
1116 2         6 $new_destination .= "/inst_root$destination";
1117 2 50       8 unless ($object->destination($new_destination))
1118             {
1119 0         0 warn "Error: Couldn't change the installation destination from $destination to $new_destination\n";
1120             }
1121             }
1122             }
1123              
1124             ################################################################################
1125             # Function: _add_objects_for_user_part()
1126             # Description: This function adds DIRECTORY objects for objects installed into
1127             # the ROOT part of the package. This is required so that the ROOT
1128             # objects are deployed correctly. Not that these objects should
1129             # not be part of the ROOT part. i.e. if you install a config file
1130             # in /etc you shouldn't be deploying the directory /etc as this is
1131             # part of the base operating system (bos).
1132             # We cannot just do a find here as we haven't created the
1133             # directory structure yet.
1134             # Arguments: None.
1135             # Returns: true on success else undef.
1136             #
1137             sub _add_objects_for_user_part
1138             {
1139 1     1   5 my $self = shift;
1140 1         13 my $tmp_dir = $self->tmp_dir();
1141              
1142 1         18 my $root_dir = "/usr/lpp";
1143 1         13 $root_dir .= "/". $self->program_name();
1144 1 50       25 if ($self->_lppmode() eq "U")
1145             {
1146 0         0 $root_dir .= "/". $self->program_name();
1147 0         0 $root_dir .= ".". $self->component_name();
1148 0         0 $root_dir .= "/". $self->version();
1149             }
1150 1         6 $root_dir .= "/inst_root";
1151              
1152             # find a list of objects that are installed in the root part.
1153 1         2 my %destinations;
1154 1         17 foreach my $object ($self->get_object_list())
1155             {
1156 23         73 my $destination = $object->destination();
1157 23 100       117 next unless $destination =~ m#$root_dir/#;
1158 3 100       42 next if $destination =~ m#$root_dir/liblpp.a#;
1159 2         7 $destinations{$destination}++;
1160             }
1161              
1162 1         7 foreach my $destination (sort keys %destinations)
1163             {
1164 2         126 my $directory = dirname($destination);
1165 2         23 while ($directory !~ m#^$root_dir$#)
1166             {
1167 4 100       15 unless (exists $destinations{$directory})
1168             {
1169 2         7 my %data;
1170 2         4 $data{'TYPE'} = 'directory';
1171 2         5 $data{'MODE'} = '0755';
1172 2         5 $data{'DESTINATION'} = "$directory";
1173 2 50       6265 unless ($self->add_item(%data))
1174             {
1175 0         0 warn "Error: Couldn't add $data{'DESTINATION'} to the package.\n";
1176             }
1177 2         10 $destinations{$directory}++;
1178             }
1179              
1180 4         187 $directory = dirname($directory);
1181             }
1182             }
1183              
1184 1         4 return 1;
1185             }
1186              
1187             1;
1188             __END__