incorporate install/remove, as well as sort fix, from James Olin Oden

CVS patchset: 6940
CVS date: 2003/07/23 13:23:34
This commit is contained in:
cturner 2003-07-23 13:23:34 +00:00
parent cc54cba032
commit c46d7c71ea
5 changed files with 639 additions and 15 deletions

View File

@ -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

View File

@ -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.

View File

@ -1,8 +1,10 @@
#include <stdio.h>
#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

View File

@ -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());

View File

@ -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);