Splint fiddles.
[tools/librpm-tizen.git] / perl-RPM2 / RPM2.pm
1 package RPM2;
2
3 use 5.00503;
4 use strict;
5 use DynaLoader;
6 use Cwd qw/realpath/;
7 use File::Basename qw/basename dirname/;
8 use File::Spec ();
9
10 use vars qw/$VERSION/;
11 $VERSION = '0.66';
12 use vars qw/@ISA/;
13 @ISA = qw/DynaLoader/;
14
15 bootstrap RPM2 $VERSION;
16
17 foreach my $tag (keys %RPM2::constants) {
18   my $sub = q {
19     sub [[method]] {
20       my $self = shift;
21       return $RPM2::constants{[[tag]]};
22     }
23   };
24
25   my $method = lc $tag;
26   $method =~ s/^rpm//;
27   $sub =~ s/\[\[method\]\]/$method/g;
28   $sub =~ s/\[\[tag\]\]/$tag/g;
29   eval $sub;
30
31   if ($@) {
32     die $@;
33   }
34 }
35
36 sub open_rpm_db {
37   my $class = shift;
38   my %params = @_;
39
40   my $self = bless { }, "RPM2::DB";
41   if ($params{-path}) {
42     $class->add_macro("_dbpath", $params{-path});
43     $self->{c_db} = RPM2::_open_rpm_db($params{-readwrite} ? 1 : 0);
44     $class->delete_macro("_dbpath");
45   }
46   else {
47     $self->{c_db} = RPM2::_open_rpm_db($params{-readwrite} ? 1 : 0);
48   }
49
50   return $self;
51 }
52
53 sub open_hdlist {
54   my $class = shift;
55   my $file = shift;
56
57   open FH, "<$file"
58     or die "Can't open $file: $!";
59
60   my @ret;
61   while (1) {
62     my ($hdr) = RPM2::_read_from_file(*FH);
63     last unless $hdr;
64
65     push @ret, RPM2::Header->_new_raw($hdr);
66   }
67
68   close FH;
69   return @ret;
70 }
71
72 sub open_package {
73   my $class = shift;
74   my $file = shift;
75   my $flags = shift;
76
77   if (RPM2->rpm_api_version > 4.0 and not defined $flags) {
78     $flags = RPM2->vsf_default;
79   }
80   $flags ||= 0;
81
82   open FH, "<$file"
83     or die "Can't open $file: $!";
84
85   my $hdr = RPM2::_read_package_info(*FH, $flags);
86   close FH;
87
88   my ($filename, $path) = (basename($file), realpath(dirname($file)));
89   $hdr = RPM2::Header->_new_raw($hdr, File::Spec->catfile($path, $filename));
90   return $hdr;
91 }
92
93 sub create_transaction
94 {
95   my $class = shift;
96   my $flags = shift;
97   my $t;
98
99   return undef if (RPM2->rpm_api_version <= 4.0); 
100   if(not defined $flags) {
101     $flags = RPM2->vsf_default;
102   }
103
104   $t = RPM2::_create_transaction($flags);
105   $t = RPM2::Transaction->_new_raw($t);
106
107   return $t;    
108 }
109
110 package RPM2::DB;
111
112 sub find_all_iter {
113   my $self = shift;
114
115   return RPM2::PackageIterator->new_iterator($self, "RPMTAG_NAME")
116 }
117
118 sub find_all {
119   my $self = shift;
120
121   return RPM2::PackageIterator->new_iterator($self)->expand_iter();
122 }
123
124 sub find_by_name_iter {
125   my $self = shift;
126   my $name = shift;
127
128   return RPM2::PackageIterator->new_iterator($self, "RPMTAG_NAME", $name);
129 }
130 sub find_by_name {
131   my $self = shift;
132   my $name = shift;
133
134   return $self->find_by_name_iter($name)->expand_iter;
135 }
136
137 sub find_by_provides_iter {
138   my $self = shift;
139   my $name = shift;
140
141   return RPM2::PackageIterator->new_iterator($self, "RPMTAG_PROVIDES", $name);
142 }
143 sub find_by_provides {
144   my $self = shift;
145   my $name = shift;
146
147   return $self->find_by_provides_iter($name)->expand_iter;
148 }
149
150 sub find_by_requires_iter {
151   my $self = shift;
152   my $name = shift;
153
154   return RPM2::PackageIterator->new_iterator($self, "RPMTAG_REQUIRENAME", $name);
155 }
156
157 sub find_by_requires {
158   my $self = shift;
159   my $name = shift;
160
161   return $self->find_by_requires_iter($name)->expand_iter;
162 }
163
164 sub find_by_file_iter {
165   my $self = shift;
166   my $name = shift;
167
168   return RPM2::PackageIterator->new_iterator($self, "RPMTAG_BASENAMES", $name);
169 }
170
171 sub find_by_file {
172   my $self = shift;
173   my $name = shift;
174
175   return $self->find_by_file_iter($name)->expand_iter;
176 }
177
178 package RPM2::Header;
179
180 use overload '<=>'  => \&op_spaceship,
181              'cmp'  => \&op_spaceship,
182              'bool' => \&op_bool;
183
184 sub _new_raw {
185   my $class = shift;
186   my $c_header = shift;
187   my $filename = shift;
188   my $offset   = shift;
189
190   my $self = bless { }, $class;
191   $self->{c_header}  = $c_header;
192   $self->{filename}  = $filename if defined $filename;
193   $self->{db_offset} = $offset   if defined $offset;
194
195   return $self;
196 }
197
198 sub tag {
199   my $self = shift;
200   my $tag = shift;
201
202   $tag = uc "RPMTAG_$tag";
203
204   die "tag $tag invalid"
205     unless exists $RPM2::header_tag_map{$tag};
206
207   return $self->{c_header}->tag_by_id($RPM2::header_tag_map{$tag});
208 }
209
210 sub tagformat {
211   my $self   = shift;
212   my $format = shift;
213
214   return RPM2::C::Header::_header_sprintf($self->{c_header}, $format);
215 }
216
217 sub compare {
218   my $h1 = shift;
219   my $h2 = shift;
220
221   return RPM2::C::Header::_header_compare($h1->{c_header}, $h2->{c_header});
222 }
223
224 sub op_bool {
225   my $self = shift;
226
227   return defined($self) && defined($self->{c_header});
228 }
229
230 sub op_spaceship {
231   my $h1 = shift;
232   my $h2 = shift;
233
234   my $ret = $h1->compare($h2);
235
236   # rpmvercmp can return any neg/pos number; normalize here to -1, 0, 1
237   return  1 if $ret > 0;
238   return -1 if $ret < 0;
239   return  0;
240 }
241
242 sub is_source_package {
243   my $self = shift;
244
245   return RPM2::C::Header::_header_is_source($self->{c_header});
246 }
247
248 sub filename {
249   my $self = shift;
250   if (exists $self->{filename}) {
251     return $self->{filename};
252   }
253   return;
254 }
255
256 sub offset {
257   my $self = shift;
258   if (exists $self->{db_offset}) {
259     return $self->{db_offset};
260   }
261   return;
262 }
263
264 sub as_nvre {
265   my $self = shift;
266   my $epoch = $self->tag('epoch');
267   my $epoch_str = '';
268
269   $epoch_str = "$epoch:" if defined $epoch;
270
271   my $ret = $epoch_str . join("-", map { $self->tag($_) } qw/name version release/);
272
273   return $ret;
274 }
275
276 foreach my $tag (keys %RPM2::header_tag_map) {
277   $tag =~ s/^RPMTAG_//g;
278
279   my $sub = q {
280     sub [[method]] {
281       my $self = shift;
282       return $self->tag("[[tag]]");
283     }
284   };
285
286   my $method = lc $tag;
287   $sub =~ s/\[\[method\]\]/$method/g;
288   $sub =~ s/\[\[tag\]\]/$tag/g;
289   eval $sub;
290
291   if ($@) {
292     die $@;
293   }
294 }
295
296 sub files {
297   my $self = shift;
298
299   if (not exists $self->{files}) {
300     my @base_names = $self->tag('basenames');
301     my @dir_names = $self->tag('dirnames');
302     my @dir_indexes = $self->tag('dirindexes');
303
304     my @files;
305     foreach (0 .. $#base_names) {
306       push @files, $dir_names[$dir_indexes[$_]] . $base_names[$_];
307     }
308
309     $self->{files} = \@files;
310   }
311
312   return @{$self->{files}};
313 }
314
315 sub changelog {
316   my $self = shift;
317
318   if (not exists $self->{changelog}) {
319     my @cltimes = $self->tag('CHANGELOGTIME');
320     my @clnames = $self->tag('CHANGELOGNAME');
321     my @cltexts = $self->tag('CHANGELOGTEXT');
322
323     my @changelog;
324     foreach (0 .. $#cltimes) {
325       push(@changelog,
326            { time => $cltimes[$_],
327              name => $clnames[$_],
328              text => $cltexts[$_],
329            });
330     }
331
332     $self->{changelog} = \@changelog;
333   }
334
335   return @{$self->{changelog}};
336 }
337
338 package RPM2::PackageIterator;
339
340 sub new_iterator {
341   my $class = shift;
342   my $db = shift;
343   my $tag = shift;
344   my $key = shift;
345
346   my $self = bless { db => $db }, $class;
347   $self->{c_iter} = RPM2::C::DB::_init_iterator($db->{c_db},
348                                                 $RPM2::header_tag_map{$tag},
349                                                 $key || "",
350                                                 defined $key ? length $key : 0);
351   return $self;
352 }
353
354 sub next {
355   my $self = shift;
356
357   return unless $self->{c_iter};
358   my ($hdr, $offset) = $self->{c_iter}->_iterator_next();
359   return unless $hdr;
360
361   my $ret = RPM2::Header->_new_raw($hdr, undef, $offset);
362   return $ret;
363 }
364
365 sub expand_iter {
366   my $self = shift;
367
368   my @ret;
369   while (my $h = $self->next) {
370     push @ret, $h;
371   }
372
373   return @ret;
374 }
375
376 # make sure c_iter is destroyed before {db} so that we always free an
377 # iterator before we free the db it came from
378
379 sub DESTROY {
380   my $self = shift;
381   delete $self->{c_iter};
382 }
383
384 package RPM2::Transaction;
385
386 sub _new_raw {
387   my $class         = shift;
388   my $c_transaction = shift;
389
390   my $self = bless { }, $class;
391   $self->{c_transaction} = $c_transaction;
392
393   return $self;
394 }
395
396 sub add_install {
397   my $self    = shift;
398   my $h       = shift;
399   my $upgrade = shift || 0;
400   my $fn;
401
402   #
403   # Must have a header to add
404   return 0 if(!defined($h));
405
406   #
407   # Get filename
408   $fn = $h->filename();
409   
410   # XXX: Need to add relocations at some point, but I think we live
411   #      without this for now (until I need it (-;).
412   return RPM2::C::Transaction::_add_install($self->{'c_transaction'}, 
413         $h->{'c_header'}, $fn, $upgrade)
414 }
415
416 sub add_erase {
417   my $self    = shift;
418   my $h       = shift;
419   my $db_offset;
420   my $fn;
421
422   #
423   # Must have a header to add
424   return 0 if(!defined($h));
425
426   #
427   # Get record offset
428   $db_offset = $h->offset();
429   return 0 if(!defined($db_offset));
430  
431   # XXX: Need to add relocations at some point, but I think we live
432   #      without this for now (until I need it (-;).
433   return RPM2::C::Transaction::_add_delete($self->{'c_transaction'}, 
434         $h->{'c_header'}, $db_offset)
435 }
436
437 sub element_count {
438         my $self = shift;
439         
440         return $self->{'c_transaction'}->_element_count();
441 }
442
443 sub close_db {
444         my $self = shift;
445         
446         return $self->{'c_transaction'}->_close_db();
447 }
448
449 sub check {
450         my $self = shift;
451         
452         return $self->{'c_transaction'}->_check();
453 }
454
455 sub order {
456         my $self = shift;
457                 
458         return $self->{'c_transaction'}->_order();
459 }
460
461 sub elements {
462         my $self = shift;
463         my $type = shift;
464
465         $type = 0 if(!defined($type));
466         
467         return $self->{'c_transaction'}->_elements($type);
468 }
469
470 sub run {
471   my $self         = shift;
472   my $ok_probs     = shift || '';
473   my $ignore_probs = shift || 0;
474
475   return RPM2::C::Transaction::_run($self->{'c_transaction'}, $ok_probs, 
476         $ignore_probs);
477 }
478
479 # Preloaded methods go here.
480
481 1;
482 __END__
483 # Below is stub documentation for your module. You better edit it!
484
485 =head1 NAME
486
487 RPM2 - Perl bindings for the RPM Package Manager API
488
489 =head1 SYNOPSIS
490
491   use RPM2;
492
493   my $db = RPM2->open_rpm_db();
494
495   my $i = $db->find_all_iter();
496   print "The following packages are installed (aka, 'rpm -qa'):\n";
497   while (my $pkg = $i->next) {
498     print $pkg->as_nvre, "\n";
499   }
500
501   $i = $db->find_by_name_iter("kernel");
502   print "The following kernels are installed (aka, 'rpm -q kernel'):\n";
503   while (my $pkg = $i->next) {
504     print $pkg->as_nvre, " ", int($pkg->size()/1024), "k\n";
505   }
506
507   $i = $db->find_by_provides_iter("kernel");
508   print "The following packages provide 'kernel' (aka, 'rpm -q --whatprovides kernel'):\n";
509   while (my $pkg = $i->next) {
510     print $pkg->as_nvre, " ", int($pkg->size()/1024), "k\n";
511   }
512
513   print "The following packages are installed (aka, 'rpm -qa' once more):\n";
514   foreach my $pkg ($db->find_by_file("/bin/sh")) {
515     print $pkg->as_nvre, "\n";
516   }
517
518   my $pkg = RPM2->open_package("/tmp/XFree86-4.1.0-15.src.rpm");
519   print "Package opened: ", $pkg->as_nvre(), ", is source: ", $pkg->is_source_package, "\n";
520
521 =head1 DESCRIPTION
522
523 The RPM2 module provides an object-oriented interface to querying both
524 the installed RPM database as well as files on the filesystem.
525
526 =head1 CLASS METHODS
527
528 Pretty much all use of the class starts here.  There are three main
529 entrypoints into the package -- either through the database of
530 installed rpms (aka the rpmdb),  through a file on the filesystem
531 (such as kernel-2.4.9-31.src.rpm or kernel-2.4.9-31.i386.rpm, or via
532 an rpm transaction.
533
534 You can have multiple RPM databases open at once, as well as running
535 multiple queries on each.  That being said if you expect to run a transaction
536 to install or erase some rpms, you will need to cause any RPM2::DB and 
537 RPM2::PackageIterator objects to go out of scope.  For instance:
538
539         $db = RPM2->open_rpm_db();
540         $i  = $db->find_by_name("vim");
541         $t  = create_transaction();
542         while($pkg = $i->next()) {
543            $t->add_erase($pkg);
544         }
545         $t->run();
546
547 Would end up in a dead lock waiting for $db, and $i (the RPM2::DB and 
548 RPM2::PackageIterator) objects to releaase their read lock on the database.
549 The correct way of handling this then would be to do the following 
550 before running the transaction:
551
552         $db = undef;
553         $i  = undef;
554
555 That is to explicitly cause the RPM2::DB and RPM2::PackageIterator objects to
556 go out of scope.
557
558 =over 4
559
560 =item open_rpm_db(-path => "/path/to/db")
561
562 As it sounds, it opens the RPM database, and returns it as an object.
563 The path to the database (i.e. C<-path>) is optional.
564
565 =item open_package("foo-1.1-14.noarch.rpm")
566
567 Opens a specific package (RPM or SRPM).  Returns a Header object.
568
569 =item create_transaction(RPM2->vsf_default)
570
571 Creates an RPM2::Transaction.  This can be used to install and 
572 remove packages.  It, also, exposes the dependency ordering functionality. 
573 It takes as an optional argument verify signature flags.  The following 
574 flags are available:
575
576 =item RPM2->vsf_default
577
578 You don't ever have to specify this, but you could if you wanted to do so.
579 This will check headers, not require a files payload, and support all the
580 various hash and signature formats that rpm supports.
581
582 =item RPM2->vsf_nohdrchk
583
584 Don't check the header.
585
586 =item RPM2->vsf_needpayload
587
588 Require that a files payload be part of the RPM (Chip is this right?).
589
590 =item RPM2->vsf_nosha1header
591
592
593 =item RPM2->vsf_nomd5header
594
595 =item RPM2->vsf_nodsaheader
596
597 =item RPM2->vsf_norsaheader
598
599 =item RPM2->vsf_nosha1
600
601 =item RPM2->vsf_nomd5
602
603 =item RPM2->vsf_nodsa
604
605 =item RPM2->vsf_norsa
606
607 =back
608
609 =head1 RPM DB object methods
610
611 =over 4
612
613 =item find_all_iter()
614
615 Returns an iterator object that iterates over the entire database.
616
617 =item find_all()
618
619 Returns an list of all of the results of the find_all_iter() method.
620
621 =item find_by_file_iter($filename)
622
623 Returns an iterator that returns all packages that contain a given file.
624
625 =item find_by_file($filename)
626
627 Ditto, except it just returns the list
628
629 =item find_by_name_iter($package_name)
630
631 You get the idea.  This one is for iterating by package name.
632
633 =item find_by_name($package_name)
634
635 Ditto, except it returns a list.
636
637 =item find_by_provides_iter($provides_string)
638
639 This one iterates over provides.
640
641 =item find_by_provides($provides_string)
642
643 Ditto, except it returns a list.
644
645 =item find_by_requires_iter($requires_string)
646
647 This one iterates over requires.
648
649 =item find_by_requires($requires_string)
650
651 Ditto, except it returns a list.
652
653 =back
654
655 =head1 RPM Database Iterator Methods
656
657 Once you have a a database iterator, then you simply need to step
658 through all the different package headers in the result set via the
659 iterator.
660
661 =over 4
662
663 =item next()
664
665 Return the next package header in the result set.
666
667 =item expand_iter()
668
669 Return the list of all the package headers in the result set of the iterator.
670
671 =back
672
673 =head1 RPM Header object methods
674
675 In addition to the following methods, all tags have simple accessors;
676 $hdr->epoch() is equivalent to $hdr->tag('epoch').
677
678 The <=> and cmp operators can be used to compare versions of two packages.
679
680 =over 4
681
682 =item $hdr->tag($tagname)
683
684 Returns the value of the tag $tagname.
685
686 =item $hdr->tagformat($format)
687
688 TODO.
689
690 =item $hdr->is_source_package()
691
692 Returns a true value if the package is a source package, false otherwise.
693
694 =item $hdr->filename()
695
696 Returns the filename of the package.
697
698 =item $hdr->offset()
699
700 Returns the rpm database offset for the package.
701
702 =item $hdr->as_nvre()
703
704 Returns a string formatted like:
705
706    epoch:name-version-release
707
708 If epoch is undefined for this package, it and the leading colon are omitted.
709
710 =item $hdr->files()
711
712 TODO.
713
714 =item $hdr->changelog()
715
716 Returns a list of hash refs containing the change log data of the package.
717 The hash keys represent individual change log entries, and their keys are:
718 C<time> (the time of the changelog entry), C<name> (the "name", ie. often
719 the email address of the author of the entry), and C<text> (the text of the
720 entry).
721
722 =back
723
724 =head1 Transaction object methods
725
726 Transactions are what allow you to install, upgrade, and remove rpms.
727 Transactions are created, have elements added to them (i.e. package headers)
728 and are ran.  When run the updates to the system and the rpm database are
729 treated as on "transaction" which is assigned a transaction id.  This can
730 be queried in install packages as the INSTALLTID, and for repackaged packages
731 they have the REMOVETID set.
732
733 =over 4
734
735 =item add_install($pkg, $upgrade)
736
737 Adds a package to a transaction for installation.  If you want this to
738 be done as a package upgrade, then be sure to set the second optional
739 parameter to 1.  It will return 0 on failure and 1 on success.  Note,
740 this should be obvious, but the package header must come from an rpm file,
741 not from the RPM database.
742
743 =item add_erase($pkg)
744
745 Adds a package to a transaction for erasure.  The package header should
746 come from the database (i.e. via an iterator) and not an rpm file.
747
748 =item element_count()
749
750 Returns the number of elements in a transaction (this is the sum of the
751 install and erase elements.
752
753 =item close_db()
754
755 Closes the rpm database.  This is needed for some ordering of
756 transactions for non-install purposes.
757
758 =item check()
759
760 Verify that the dependencies for this transaction are met.  Returns
761 0 on failure and 1 on success.
762
763 =item order()
764
765 Order the elements in dependency order.
766
767 =item elements()
768
769 Return a list of elements as they are presently ordered.  Note, this
770 returns the NEVR's not the package headers.
771
772 =item run()
773
774 Run the transaction.  This will automatically check for dependency
775 satisfaction, and order the transaction.
776
777 =back
778
779 =head1 TODO
780
781 Make package installation and removal better (-;.
782
783 Signature validation.
784
785 =head1 HISTORY
786
787 =over 8
788
789 =item 0.01
790 Initial release
791
792 =back
793
794
795 =head1 AUTHOR
796
797 Chip Turner E<lt>cturner@redhat.comE<gt>
798
799 =head1 SEE ALSO
800
801 L<perl>.
802 The original L<RPM> module.
803
804 =cut