From c46d7c71ea0bd2ad95842f4023680402a6570cd4 Mon Sep 17 00:00:00 2001 From: cturner Date: Wed, 23 Jul 2003 13:23:34 +0000 Subject: incorporate install/remove, as well as sort fix, from James Olin Oden CVS patchset: 6940 CVS date: 2003/07/23 13:23:34 --- perl-RPM2/Changes | 5 + perl-RPM2/RPM2.pm | 274 +++++++++++++++++++++++++++++++++++++++++++++++++-- perl-RPM2/RPM2.xs | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++- perl-RPM2/test.pl | 62 ++++++++++++ perl-RPM2/typemap | 23 +++++ 5 files changed, 639 insertions(+), 15 deletions(-) (limited to 'perl-RPM2') diff --git a/perl-RPM2/Changes b/perl-RPM2/Changes index 0a6efddb3..d57a3aacf 100644 --- a/perl-RPM2/Changes +++ b/perl-RPM2/Changes @@ -1,3 +1,8 @@ +0.63 2003-03-16 cturner + * add support for install/erase from James Olin Oden; experimental but looks solid. API may change. + + * Fix sort bug found by James Olin Oden + 0.62 2003-03-16 cturner * fix a few rpmlib 4.0.4/perl 5.6.x issues diff --git a/perl-RPM2/RPM2.pm b/perl-RPM2/RPM2.pm index 1abf9af81..8b9260462 100644 --- a/perl-RPM2/RPM2.pm +++ b/perl-RPM2/RPM2.pm @@ -72,6 +72,23 @@ sub open_package { return $hdr; } +sub create_transaction +{ + my $class = shift; + my $flags = shift; + my $t; + + return undef if (RPM2->rpm_api_version <= 4.0); + if(not defined $flags) { + $flags = RPM2->vsf_default; + } + + $t = RPM2::_create_transaction($flags); + $t = RPM2::Transaction->_new_raw($t); + + return $t; +} + package RPM2::DB; sub find_all_iter { @@ -142,17 +159,20 @@ sub find_by_file { package RPM2::Header; -use overload '<=>' => \&op_spaceship, +use overload '<=>' => \&op_spaceship, + 'cmp' => \&op_spaceship, 'bool' => \&op_bool; sub _new_raw { my $class = shift; my $c_header = shift; my $filename = shift; + my $offset = shift; my $self = bless { }, $class; - $self->{c_header} = $c_header; - $self->{filename} = $filename if defined $filename; + $self->{c_header} = $c_header; + $self->{filename} = $filename if defined $filename; + $self->{db_offset} = $offset if defined $offset; return $self; } @@ -215,6 +235,14 @@ sub filename { return; } +sub offset { + my $self = shift; + if (exists $self->{db_offset}) { + return $self->{db_offset}; + } + return; +} + sub as_nvre { my $self = shift; my $epoch = $self->tag('epoch'); @@ -286,10 +314,10 @@ sub next { my $self = shift; return unless $self->{c_iter}; - my $hdr = $self->{c_iter}->_iterator_next(); + my ($hdr, $offset) = $self->{c_iter}->_iterator_next(); return unless $hdr; - my $ret = RPM2::Header->_new_raw($hdr); + my $ret = RPM2::Header->_new_raw($hdr, undef, $offset); return $ret; } @@ -312,6 +340,101 @@ sub DESTROY { delete $self->{c_iter}; } +package RPM2::Transaction; + +sub _new_raw { + my $class = shift; + my $c_transaction = shift; + + my $self = bless { }, $class; + $self->{c_transaction} = $c_transaction; + + return $self; +} + +sub add_install { + my $self = shift; + my $h = shift; + my $upgrade = shift || 0; + my $fn; + + # + # Must have a header to add + return 0 if(!defined($h)); + + # + # Get filename + $fn = $h->filename(); + + # XXX: Need to add relocations at some point, but I think we live + # without this for now (until I need it (-;). + return RPM2::C::Transaction::_add_install($self->{'c_transaction'}, + $h->{'c_header'}, $fn, $upgrade) +} + +sub add_erase { + my $self = shift; + my $h = shift; + my $db_offset; + my $fn; + + # + # Must have a header to add + return 0 if(!defined($h)); + + # + # Get record offset + $db_offset = $h->offset(); + return 0 if(!defined($db_offset)); + + # XXX: Need to add relocations at some point, but I think we live + # without this for now (until I need it (-;). + return RPM2::C::Transaction::_add_delete($self->{'c_transaction'}, + $h->{'c_header'}, $db_offset) +} + +sub element_count { + my $self = shift; + + return $self->{'c_transaction'}->_element_count(); +} + +sub close_db { + my $self = shift; + + return $self->{'c_transaction'}->_close_db(); +} + +sub check { + my $self = shift; + + return $self->{'c_transaction'}->_check(); +} + +sub order { + my $self = shift; + + return $self->{'c_transaction'}->_order(); +} + +sub elements { + my $self = shift; + my $type = shift; + + $type = 0 if(!defined($type)); + + return $self->{'c_transaction'}->_elements($type); +} + +sub run { + my $self = shift; + my $ok_probs = shift || ''; + my $ignore_probs = shift || 0; + + return RPM2::C::Transaction::_run($self->{'c_transaction'}, $ok_probs, + $ignore_probs); +} + # Preloaded methods go here. 1; @@ -361,22 +484,88 @@ the installed RPM database as well as files on the filesystem. =head1 CLASS METHODS -Pretty much all use of the class starts here. There are two main +Pretty much all use of the class starts here. There are three main entrypoints into the package -- either through the database of -installed rpms (aka the rpmdb) or through a file on the filesystem -(such as kernel-2.4.9-31.src.rpm or kernel-2.4.9-31.i386.rpm +installed rpms (aka the rpmdb), through a file on the filesystem +(such as kernel-2.4.9-31.src.rpm or kernel-2.4.9-31.i386.rpm, or via +an rpm transaction. You can have multiple RPM databases open at once, as well as running -multiple queries on each. +multiple queries on each. That being said if you expect to run a transaction +to install or erase some rpms, you will need to cause any RPM2::DB and +RPM2::PackageIterator objects to go out of scope. For instance: + + $db = RPM2->open_rpm_db(); + $i = $db->find_by_name("vim"); + $t = create_transaction(); + while($pkg = $i->next()) { + $t->add_erase($pkg); + } + $t->run(); + +Would end up in a dead lock waiting for $db, and $i (the RPM2::DB and +RPM2::PackageIterator) objects to releaase their read lock on the database. +The correct way of handling this then would be to do the following +before running the transaction: + + $db = undef; + $i = undef; + +That is to explicitly cause the RPM2::DB and RPM2::PackageIterator objects to go +out of scope. + =item open_rpm_db(-path => "/path/to/db") As it sounds, it opens the RPM database, and returns it as an object. +The path to the database (i.e. C<-path>) is optional. =item open_package("foo-1.1-14.noarch.rpm") Opens a specific package (RPM or SRPM). Returns a Header object. +=item create_transaction(RPM2->vsf_default) + +Creates an RPM2::Transaction. This can be used to install and +remove packages. It, also, exposes the dependency ordering functionality. +It takes as an optional argument verify signature flags. The following +flags are available: + +=over 4 + +=item RPM2->vsf_default + +You don't ever have to specify this, but you could if you wanted to do so. +This will check headers, not require a files payload, and support all the +various hash and signature formats that rpm supports. + +=item RPM2->vsf_nohdrchk + +Don't check the header. + +=item RPM2->vsf_needpayload + +Require that a files payload be part of the RPM (Chip is this right?). + +=item RPM2->vsf_nosha1header + + +=item RPM2->vsf_nomd5header + +=item RPM2->vsf_nodsaheader + +=item RPM2->vsf_norsaheader + +=item RPM2->vsf_nosha1 + +=item RPM2->vsf_nomd5 + +=item RPM2->vsf_nodsa + +=item RPM2->vsf_norsa + +=back + =head1 RPM DB object methods =item find_all_iter() @@ -419,13 +608,78 @@ This one iterates over requires. Ditto, except it returns a list. +=head1 RPM Database Iterator Methods + +Once you have a a database iterator, then you simply need to step +through all the different package headers in the result set via the +iterator. + +=item next() + +Return the next package header in the result set. + +=item expand_iter + +Return the list of all the package headers in the result set of the iterator. + =head1 RPM Header object methods stuff goes here +=head1 Transaction object methods + +Transactions are what allow you to install, upgrade, and remove rpms. +Transactions are created, have elements added to them (i.e. package headers) +and are ran. When run the updates to the system and the rpm database are +treated as on "transaction" which is assigned a transaction id. This can +be queried in install packages as the INSTALLTID, and for repackaged packages +they have the REMOVETID set. + +=item add_install($pkg, $upgrade) + +Adds a package to a transaction for installation. If you want this to +be done as a package upgrade, then be sure to set the second optional +parameter to 1. It will return 0 on failure and 1 on success. Note, +this should be obvious, but the package header must come from an rpm file, +not from the RPM database. + +=item add_erase($pkg) + +Adds a package to a transaction for erasure. The package header should +come from the database (i.e. via an iterator) and not an rpm file. + +=item element_count() + +Returns the number of elements in a transaction (this is the sum of the +install and erase elements. + +=item close_db() + +Closes the rpm database. This is needed for some ordering of +transactions for non-install purposes. + +=item check() + +Verify that the dependencies for this transaction are met. Returns +0 on failure and 1 on success. + +=item order() + +Order the elements in dependency order. + +=item elements() + +Return a list of elements as they are presently ordered. Note, this returns the +NEVR's not the package headers. + +=item run() + +Run the transaction. This will automatically check for dependency satisfaction, and +order the transaction. + =head1 TODO -Package installation and removal. +Make package installation and removal better (-;. Signature validation. diff --git a/perl-RPM2/RPM2.xs b/perl-RPM2/RPM2.xs index 4cb7f3399..919262e45 100644 --- a/perl-RPM2/RPM2.xs +++ b/perl-RPM2/RPM2.xs @@ -1,8 +1,10 @@ +#include #include "rpmlib.h" #include "rpmcli.h" #ifdef RPM2_RPM41 #include "rpmts.h" +#include "rpmte.h" #endif #include "header.h" @@ -17,6 +19,113 @@ #error Must define one of RPM2_RPM41 or RPM2_RPM40; perhaps Makefile.PL could not guess your RPM API version? #endif +/* Chip, this is somewhat stripped down from the default callback used by + the rpmcli. It has to be here to insure that we open the pkg again. + If we don't do this we get segfaults. I also, kept the updating of some + of the rpmcli static vars, but I may not have needed to do this. + + Also, we probably want to give a nice interface such that we could allow + users of RPM2 to do their own callback, but that will have to come later. +*/ +void * _null_callback( + const void * arg, + const rpmCallbackType what, + const unsigned long amount, + const unsigned long total, + fnpyKey key, + rpmCallbackData data) +{ + Header h = (Header) arg; + char * s; + int flags = (int) ((long)data); + void * rc = NULL; + const char * filename = (const char *)key; + static FD_t fd = NULL; + int xx; + + /* Code stolen from rpminstall.c and modified */ + switch(what) { + case RPMCALLBACK_INST_OPEN_FILE: + if (filename == NULL || filename[0] == '\0') + return NULL; + fd = Fopen(filename, "r.ufdio"); + /* FIX: still necessary? */ + if (fd == NULL || Ferror(fd)) { + fprintf(stderr, "open of %s failed!\n", filename); + if (fd != NULL) { + xx = Fclose(fd); + fd = NULL; + } + } else + fd = fdLink(fd, "persist (showProgress)"); + return (void *)fd; + break; + + case RPMCALLBACK_INST_CLOSE_FILE: + /* FIX: still necessary? */ + fd = fdFree(fd, "persist (showProgress)"); + if (fd != NULL) { + xx = Fclose(fd); + fd = NULL; + } + break; + + case RPMCALLBACK_INST_START: + rpmcliHashesCurrent = 0; + if (h == NULL || !(flags & INSTALL_LABEL)) + break; + break; + + case RPMCALLBACK_TRANS_PROGRESS: + case RPMCALLBACK_INST_PROGRESS: + break; + + case RPMCALLBACK_TRANS_START: + rpmcliHashesCurrent = 0; + rpmcliProgressTotal = 1; + rpmcliProgressCurrent = 0; + break; + + case RPMCALLBACK_TRANS_STOP: + rpmcliProgressTotal = rpmcliPackagesTotal; + rpmcliProgressCurrent = 0; + break; + + case RPMCALLBACK_REPACKAGE_START: + rpmcliHashesCurrent = 0; + rpmcliProgressTotal = total; + rpmcliProgressCurrent = 0; + break; + + case RPMCALLBACK_REPACKAGE_PROGRESS: + break; + + case RPMCALLBACK_REPACKAGE_STOP: + rpmcliProgressTotal = total; + rpmcliProgressCurrent = total; + rpmcliProgressTotal = rpmcliPackagesTotal; + rpmcliProgressCurrent = 0; + break; + + case RPMCALLBACK_UNINST_PROGRESS: + break; + case RPMCALLBACK_UNINST_START: + break; + case RPMCALLBACK_UNINST_STOP: + break; + case RPMCALLBACK_UNPACK_ERROR: + break; + case RPMCALLBACK_CPIO_ERROR: + break; + case RPMCALLBACK_UNKNOWN: + break; + default: + break; + } + + return rc; +} + void _populate_header_tags(HV *href) { @@ -65,6 +174,8 @@ BOOT: REGISTER_CONSTANT(_RPMVSF_NOSIGNATURES); REGISTER_CONSTANT(_RPMVSF_NOHEADER); REGISTER_CONSTANT(_RPMVSF_NOPAYLOAD); + REGISTER_CONSTANT(TR_ADDED); + REGISTER_CONSTANT(TR_REMOVED); #endif } @@ -164,6 +275,30 @@ _read_package_info(fp, vsflags) ts = rpmtsFree(ts); #endif +void +_create_transaction(vsflags) + int vsflags + PREINIT: + rpmts ret; + PPCODE: + /* Looking at librpm, it does not look like this ever + returns error (though maybe it should). + */ + ret = rpmtsCreate(); + + /* Should I save the old vsflags aside? */ + rpmtsSetVSFlags(ret, vsflags); + + /* Convert and throw the results on the stack */ + SV *h_sv; + + EXTEND(SP, 1); + + h_sv = sv_newmortal(); + sv_setref_pv(h_sv, "RPM2::C::Transaction", (void *)ret); + + PUSHs(h_sv); + rpmdb _open_rpm_db(for_write) int for_write @@ -208,14 +343,23 @@ Header _iterator_next(i) rpmdbMatchIterator i PREINIT: - Header ret; - CODE: + Header ret; + unsigned int offset; + PPCODE: ret = rpmdbNextIterator(i); if (ret) headerLink(ret); - RETVAL = ret; - OUTPUT: - RETVAL + if(ret != NULL) + offset = rpmdbGetIteratorOffset(i); + else + offset = 0; + + EXTEND(SP, 2); + SV * h_sv; + h_sv = sv_newmortal(); + sv_setref_pv(h_sv, "RPM2::C::Header", (void *)ret); + PUSHs(h_sv); + PUSHs(sv_2mortal(newSViv(offset))); void DESTROY(i) @@ -315,3 +459,139 @@ _header_sprintf(h, format) s = headerSprintf(h, format, rpmTagTable, rpmHeaderFormats, NULL); PUSHs(sv_2mortal(newSVpv((char *)s, 0))); s = _free(s); + + +MODULE = RPM2 PACKAGE = RPM2::C::Transaction + +void +DESTROY(t) + rpmts t + CODE: + t = rpmtsFree(t); + +# XXX: Add relocations some day. +int +_add_install(t, h, fn, upgrade) + rpmts t + Header h + char * fn + int upgrade + PREINIT: + rpmRC rc = 0; + CODE: + rc = rpmtsAddInstallElement(t, h, (fnpyKey) fn, upgrade, NULL); + RETVAL = (rc == RPMRC_OK) ? 1 : 0; + OUTPUT: + RETVAL + +int +_add_delete(t, h, offset) + rpmts t + Header h + unsigned int offset + PREINIT: + rpmRC rc = 0; + CODE: + rc = rpmtsAddEraseElement(t, h, offset); + RETVAL = (rc == RPMRC_OK) ? 1 : 0; + OUTPUT: + RETVAL + +int +_element_count(t) + rpmts t +PREINIT: + int ret; +CODE: + ret = rpmtsNElements(t); + RETVAL = ret; +OUTPUT: + RETVAL + +int +_close_db(t) + rpmts t +PREINIT: + int ret; +CODE: + ret = rpmtsCloseDB(t); + RETVAL = (ret == 0) ? 1 : 0; +OUTPUT: + RETVAL + +int +_check(t) + rpmts t +PREINIT: + int ret; +CODE: + ret = rpmtsCheck(t); + RETVAL = (ret == 0) ? 1 : 0; +OUTPUT: + RETVAL + +int +_order(t) + rpmts t +PREINIT: + int ret; +CODE: + ret = rpmtsOrder(t); + /* XXX: May want to do something different here. It actually + returns the number of non-ordered elements...maybe we + want this? + */ + RETVAL = (ret == 0) ? 1 : 0; +OUTPUT: + RETVAL + +void +_elements(t, type) + rpmts t; + rpmElementType type; +PREINIT: + rpmtsi i; + rpmte te; + const char * NEVR; +PPCODE: + i = rpmtsiInit(t); + if(i == NULL) { + printf("Did not get a thing!\n"); + return; + } else { + while((te = rpmtsiNext(i, type)) != NULL) { + NEVR = rpmteNEVR(te); + XPUSHs(sv_2mortal(newSVpv(NEVR, 0))); + } + i = rpmtsiFree(i); + } + +int +_run(t, ok_probs, prob_filter) + rpmts t + rpmprobFilterFlags prob_filter + PREINIT: + int i; + rpmProblem p; + int ret; + CODE: + /* Make sure we could run this transactions */ + ret = rpmtsCheck(t); + if (ret != 0) { + RETVAL = 0; + return; + } + ret = rpmtsOrder(t); + if (ret != 0) { + RETVAL = 0; + return; + } + + /* XXX: Should support callbacks eventually */ + (void) rpmtsSetNotifyCallback(t, _null_callback, (void *) ((long)0)); + ret = rpmtsRun(t, NULL, prob_filter); + RETVAL = (ret == 0) ? 1 : 0; + OUTPUT: + RETVAL + + diff --git a/perl-RPM2/test.pl b/perl-RPM2/test.pl index 5b295cde5..ff3229f01 100644 --- a/perl-RPM2/test.pl +++ b/perl-RPM2/test.pl @@ -105,6 +105,7 @@ my $other_rpm_dir = "/usr/lib/rpmdb/i386-redhat-linux/redhat"; if (-d $other_rpm_dir) { my $db2 = RPM2->open_rpm_db(-path => $other_rpm_dir); ok(defined $db2); + $db2 = undef; } else { print "Install the rpmdb-redhat package to test two simultaneous open databases\n"; @@ -120,3 +121,64 @@ ok(RPM2->expand_macro("%rpm2_test_macro") eq "%rpm2_test_macro"); ok(RPM2->rpm_api_version == 4.1 or RPM2->rpm_api_version == 4.0); ok(RPM2->rpm_api_version == 4.0 or RPM2->vsf_nosha1 == 65536); +# +# Clean up before transaction tests (close the database +$db = undef; +$i = undef; + +# +# Transaction tests. +my $t = RPM2->create_transaction(); +ok(ref($t) eq 'RPM2::Transaction'); +ok(ref($t) eq 'RPM2::Transaction'); +$pkg = RPM2->open_package("test-rpm-1.0-1.noarch.rpm"); +# Make sure we still can open packages. +ok($pkg); +# Add package to transaction +ok($t->add_install($pkg)); +# Check element count +ok($t->element_count() == 1); +# Test depedency checks +ok($t->check()); +# Order the transaction...see if we get our one transaction. +ok($t->order()); +my @rpms = $t->elements(); +ok($rpms[0] eq $pkg->as_nvre()); +ok(scalar(@rpms) == 1); +# Install package +ok($t->run()); +$t = undef; + +# +# See if we can find the rpm in the database now... +$db = RPM2->open_rpm_db(); +ok(defined $db); +@pkg = (); +$i = $db->find_by_name_iter("test-rpm"); +ok($i); +while (my $pkg = $i->next) { + push @pkg, $pkg; +} +ok(scalar(@pkg) == 1); +$i = undef; +$db = undef; + +# +# OK, lets remove that rpm with a new transaction +my $t = RPM2->create_transaction(); +ok(ref($t) eq 'RPM2::Transaction'); +# We need to find the package we installed, and try to erase it +ok($t->add_erase($pkg[0])); +# Check element count +ok($t->element_count() == 1); +# Test depedency checks +ok($t->check()); +# Order the transaction...see if we get our one transaction. +ok($t->order()); +#my @rpms = $t->elements(); +ok($rpms[0] eq $pkg->as_nvre()); +ok(scalar(@rpms) == 1); +# Install package +ok($t->run()); +# Test closing the database +ok($t->close_db()); diff --git a/perl-RPM2/typemap b/perl-RPM2/typemap index f93ef29a6..4570844f7 100644 --- a/perl-RPM2/typemap +++ b/perl-RPM2/typemap @@ -2,6 +2,9 @@ TYPEMAP rpmdb O_OBJECT_rpmdb rpmdbMatchIterator O_OBJECT_rpmmi Header O_OBJECT_header +rpmts O_OBJECT_rpmts +rpmprobFilterFlags O_OBJECT_rpmprob_filter_flags +rpmElementType O_OBJECT_rpm_element_type INPUT O_OBJECT_rpmdb @@ -28,6 +31,20 @@ O_OBJECT_header XSRETURN_UNDEF; } +O_OBJECT_rpmts + if (sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG)) + $var = ($type)SvIV((SV*)SvRV( $arg )); + else { + warn( \"${Package}::$func_name() -- $var is not a blessed SV reference\" ); + XSRETURN_UNDEF; + } + +O_OBJECT_rpmprob_filter_flags + $var = ($type) SvIV($arg); + +O_OBJECT_rpm_element_type + $var = ($type) SvIV($arg); + OUTPUT O_OBJECT_rpmdb sv_setref_pv( $arg, "RPM2::C::DB", (void*)$var ); @@ -35,3 +52,9 @@ O_OBJECT_rpmmi sv_setref_pv( $arg, "RPM2::C::PackageIterator", (void*)$var ); O_OBJECT_header sv_setref_pv( $arg, "RPM2::C::Header", (void*)$var ); +O_OBJECT_rpmts + sv_setref_pv( $arg, "RPM2::C::Transaction", (void*)$var ); +O_OBJECT_rpmprob_filter_flags + sv_setiv($arg, (IV)$var); +O_OBJECT_rpm_element_type + sv_setiv($arg, (IV)$var); -- cgit v1.2.3