Nylig måtte gjenfinne jeg detaljer om programvarepakker installert på Fedora 15, Rød Hattvirksomhet Linux, Centos og andre distribusjoner som fordeler deres programvarepakker som bruker det RPMPakke Sjef. Til min overraskelse, hva burde ha vært en relativt enkel oppgave som ha blitt vendt ut å være helt rotet på grunn av forandringer i RPM bibliotek APIs og innvendig format over de siste få årene. I denne posten demonstrerer jeg hvordan å gjenfinne informasjon om RPM pakker bruke C og Pyton.
RPM er en kommando-ledning eller API kjørt pakkeledelsessystem i stand til å installere, avinstallere, bekrefte, querying, og å oppdatering av Linux eller Unix programvarepakker. Hver programvarepakke består av et arkiv av arkivene sammen med informasjon om pakken slik som dets versjonsantall, et sammendrag og en beskrivelse, og avhengighetsinformasjon. Det er også et bibliotek som API å gjøre fremkallere i stand til å forvalte slik transaksjoner fra kompilert programmere språk slik som C eller scripting språk slik Pyton. Pakkearkiver er skrevet til skive i nettverksbyterekkefølge. Hvis nødvendig forvandler RPM automatisk data til vertbyterekkefølge når pakken arkivet er lest.
RPM ble første utviklet i 1997 ved Erik Troan og Marc Ewing for bruk i den Røde Hatt Linux distribusjon. I mange årene var det et opensource prosjekt som har ikke mottatt mye kjærligheten eller oppmerksomhet. Det bildet forandret I tidlig 2007 når to separat (og konkurrere) utviklingsfellesskap ble sjøsatt.
Den mer framstående RPM utviklingsfellesskap er rpm.organisasjon Hvilken er LED ved Rød Hatt. Gir til deres website :
Etter en lang utviklingsbruddrpm.organisasjon ble relansert i 2007 med målet å gjenvinne posisjonen som oppstrøms hjem av RPM. Mens en første trinnlapper som hadde stablet opp i de forskjellige distribusjonene ha blitt integrert inn i koden basisen så fjern som mulig. Vi vil at RPM ikke er provinsen av en selskap, eller et lite sett av fremkallerne. Det må bli utviklet i et åpent fellesskap, fortært og bidratt til ved mange selskaper, brukere, distribusjoner, og fremkallere. Vi ønsker derfor velkommen noen og alle bidragsytere.
….
RPM vil bli tilbakestående forenelig til 4.4.2 for en helt lang tid. Det er nødvendig at tredjeparts pakker kan bli installert uten behovet å rekompilere dem – spesielt for virksomhetsdistribusjoner.
I mai 2007 Røde Hatter ansatte Panu Matilainen Å bearbeide RPM prosjekt. Den første hovedkode revisjonen var i juli 2007 ; versjon 4,8 ble utløst i januar 2010, og 4,9 i mars 2011. Denne versjonen blir brukt av distribusjoner slik som Fedora, Rød Hattvirksomhet Linux, opensuse, SUSE Linux Virksomhet og Centos.
Den andre RPM utviklingsfellesskap er rpm5.org Av hvilken blir ledet Jeff Johnson som var en vedlikeholder av RPM whilst en arbeidstaker av Rød Hatt. RPMversjon 5,0 ble utløst i mai 2007. Deres senest versjon er 5.3.11 daterte 02-jun-2011. Denne versjonen av RPM blir brukt av distribusjoner slik som Enhet Linux og cAos Linux, og også ved Openpkg prosjekt som gir pakker for noe annet Unix-Fortrinnsvalg plattformer. Tydelig Mandriva nylig har koplet til det også skjønt der synes å være noen kontrovers Om den spesielle avgjørelsen.
Formatet av en RPM pakke er binært og består av tre kapitler i den følgende rekkefølgen :
- Blyet kapittelet identifiserer arkivet mens et RPM arkiv. Det inneholder flere foreldet samlerør som i forrige forrige versjoner av RPM ble brukt om å lagre informasjon brukt innvendig av RPM. I dag imidlertid er blyet kapittelet’s bare formål å lage det lett å identifisere et RPM pakkearkiv.
- Underskriften kapittelet inneholder informasjon som kan bli brukt om å bekrefte integriteten, og valgfrit, autentisiteten av majoriteten av pakken. Dette kapittelet er iverksatt å bruking en samlerørstruktur (se under).
- Samlerøret kapittelet inneholder pakken metadata slik som navn, versjon, arkitektur, liste av inkluderede arkiver og suchlike. Det er også iverksatt som en samlerørstruktur.
- Th endelig kapittel inneholder det aktuelle arkiv arkivet, som er vanligvis i cpio format, komprimerte med gzip men mer nylige versjoner av RPM kan også bruke bzip2, lzma eller xz kompresjon og xar (XML Arkiv) blir støttet av RPM 5,0.
Her er hva blyet inneholder. Bruk ingenting som helst fra blyet unntatt hovedantallet og underskrifttype.
struct rpmlead_s {
unsigned char magic[4];
unsigned char major;
unsigned char minor;
short type;
short archnum;
char name[66];
short osnum;
short signature_type; /*!< Signature header type (RPMSIG_HEADERSIG) */
char reserved[16]; /*!< Pad to 96 bytes -- 8 byte aligned! */
};
Samlerøret strukturen begrepet er RPMS løsning til problemet av lett manipulere informasjon i en normeret måte. Formålet av en samlerørstruktur er å inneholde null eller mer stykker data. Det er tre kapitler til hver samlerørstruktur. Første kapittelet er visst som samlerøret strukturen samlerøret. Samlerøret strukturen samlerøret blir brukt om å identifisere starten av en samlerørstruktur, dets størrelse, og antallet datating som det inneholder. Følgende samlerøret strukturen samlerøret er et område kalte indeksen.
Samlerøret består strukturs indeks av null eller mer indeksadganger. Hver adgang er seksten byter lang. De første fire bytene inneholder et merke en numerisk verdi som identifiserer hvilken typen dataene blir pekt på ved adgangen. Det er et stort antall samlerørmerker som blir definert i rpmtag.h. Her er et par av dem :
typedef enum rpmTag_e {
....
RPMTAG_NAME = 1000, /* s */
RPMTAG_VERSION = 1001, /* s */
RPMTAG_RELEASE = 1002, /* s */
RPMTAG_EPOCH = 1003, /* i */
RPMTAG_SUMMARY = 1004, /* s{} */
RPMTAG_DESCRIPTION = 1005, /* s{} */
RPMTAG_BUILDTIME = 1006, /* i */
RPMTAG_BUILDHOST = 1007, /* s */
RPMTAG_INSTALLTIME = 1008, /* i */
RPMTAG_SIZE = 1009, /* i */
RPMTAG_DISTRIBUTION = 1010, /* s */
RPMTAG_VENDOR = 1011, /* s */
.....
/* tags 1997-4999 reserved */
RPMTAG_FILENAMES = 5000, /* s[] extension */
RPMTAG_FILEPROVIDE = 5001, /* s[] extension */
RPMTAG_FILEREQUIRE = 5002, /* s[] extension */
RPMTAG_FSNAMES = 5003, /* s[] (unimplemented) */
RPMTAG_FSSIZES = 5004, /* l[] (unimplemented) */
RPMTAG_TRIGGERCONDS = 5005, /* s[] extension */
RPMTAG_TRIGGERTYPE = 5006, /* s[] extension */
RPMTAG_ORIGFILENAMES = 5007, /* s[] extension */
RPMTAG_LONGFILESIZES = 5008, /* l[] */
RPMTAG_LONGSIZE = 5009, /* l */
} rpmTag;
Følgende hvert merke, er en fire-bytetype, som er en numerisk verdi som beskriver formatet av dataene som blir pekt på ved adgangen. Her er den nåværende listen av typer-definert i rpmtag.h :
typedef enum rpmTagType_e {
RPM_NULL_TYPE = 0,
RPM_CHAR_TYPE = 1,
RPM_INT8_TYPE = 2,
RPM_INT16_TYPE = 3,
RPM_INT32_TYPE = 4,
RPM_INT64_TYPE = 5,
RPM_STRING_TYPE = 6,
RPM_BIN_TYPE = 7,
RPM_STRING_ARRAY_TYPE = 8,
RPM_I18NSTRING_TYPE = 9,
} rpmTagType;
Mest av disse typene burde være selv forklarende. Forskjellen mellom en SNOR type og en STRING_ARRAY type er at det tidligere er et regulært null avslutt snor mens det sistnevnte er en innsamling av de snorer. RPM_I18NSTRING_TYPE er nedsatt.
Neste er en 4-bytes motvektverdi som inneholder den aktuelle posisjonen av dataene, slektning til begynnelsen av forretningen. Til slutt er det en fire-byteopptelling som inneholder antallet datating pekte på ved indeksen adgangen. SNORdata har alltid en opptelling av 1, mens STRING_ARRAY data har en opptelling lik antallet snorer inneholdt i forretningen.
Etter indeksen kommer forretningen. Det er i forretningen som aktuelle datating er beholdt. Dataene i forretningen er pakket inn sammen som nær mens mulig i nettverksbyterekkefølge, dvs. mest signifikante byter først. SNORdata er avsluttet med en ugyldig byte. Heltallsdata er lagret på den naturlige grensen for dets type, dvs. et 32-bits heltall er lagret på en 4-bytes grense.
Ok, tid å snakke om hvordan programmatiskt å komme til RPM pakker. Men før du starter til å bruke RPM bibliotek, må du utpønske hvilken versjonen av RPM bibliotek du bruker. Hvis du skriver en mantelskrifttype, dette er lett å gjøre. For eksempel på Fedora 15 :
$ /usr/lib/rpm/rpmdeps --version RPM version 4.9.0
RPM bibliotek fra rpm5.org har rpmlibversion API som kan bli brukt om å gjenfinne biblioteket versjonen eller deg kan enkelt komme til RPMVERSION snor som vist nedenfor.
#include <stdio.h>
#include <rpm/rpmlib.h>
int
main()
{
fprintf(stderr, "RPM Version: %s\n", RPMVERSION);
}
Dessverre har ikke RPM bibliotek fra rpm.organisasjon noe som helst liknende. Bemerk til rpm.organisasjons fremkallere ! Vær så snill å tilføy rpmlibversion API til din liste av støttet offentlig APIs slik at anvendelser som trenger programmatiskt å komme til RPM pakker og databaser kan lett utpønske som smak og utløsning av et RPM bibliotek de behandler og justerer deres kode deretter.
På Fedora 15 er versjonen snoret som er returnert 4.9.0 som angir en versjon av RPM som ble utløst i mars 2011. På Centos 5,6 er versjonen snoret som er returnert 4.4.2.3 som angir en signifikant eldre versjon av RPM utløst i April 2008. Det er signifikant og hovedforskjeller mellom disse versjonene. I spesiell C koder skrevet for en av disse to spesielle RPM versjoner sannsynlig vil ikke arbeide for den andre versjonen uten modifikasjon.
Det får verre forresten som Fedora 15 og Rød Hattvirksomhet Linux 6 bruker den nyere RPM biblioteket som også inkluderer et nytt format og SHA Hasj. Dette forårsaker problemer når du prøver å installere en RPM som blir bygd på en av disse plattformene på en eldre plattform slik som Centos 5,6 som ikke vet om det nye formatet og hasjen. Derfor vil Centos 5,6 klage at det kan ikke bekrefte integriteten av RPM pakke.
Du må også kunne fastsette hva kjennetegner en spesiell versjon av RPM støtter. Her er enveiskjørt av å gjøre det :
#include <stdio.h>
#include <stdlib.h>
#include <rpm/rpmlib.h>
#include <rpm/rpmds.h>
int
main(int argc, char *argv[])
{
const char *DNEVR;
rpmds ds = NULL;
int rc;
rpmReadConfigFiles(NULL, NULL);
rc = rpmdsRpmlib(&ds, NULL);
ds = rpmdsInit(ds);
fprintf(stdout, "Supported features:\n");
while (rpmdsNext(ds) >= 0) {
if ((DNEVR = rpmdsDNEVR(ds)) != NULL)
fprintf(stdout, "%s\n", DNEVR + 2);
}
ds = rpmdsFree(ds);
exit(0);
}
Du må ha RPM utviklingspakke (rpm-devel) installerte i rekkefølge kunne kompilere den ovenfor koden.
Denne koden trykker enkelt den støttede RPM kjennetegnet satte. Her er hva er ytet for RPM bibliotek på Fedora 15 :
Supported features: rpmlib(BuiltinLuaScripts) = 4.2.2-1 rpmlib(CompressedFileNames) = 3.0.4-1 rpmlib(ConcurrentAccess) = 4.1-1 rpmlib(ExplicitPackageProvide) = 4.0-1 rpmlib(FileCaps) = 4.6.1-1 rpmlib(FileDigests) = 4.6.0-1 rpmlib(HeaderLoadSortsTags) = 4.0.1-1 rpmlib(PartialHardlinkSets) = 4.0.4-1 rpmlib(PayloadFilesHavePrefix) = 4.0-1 rpmlib(PayloadIsBzip2) = 3.0.5-1 rpmlib(PayloadIsLzma) = 4.4.2-1 rpmlib(PayloadIsXz) = 5.2-1 rpmlib(ScriptletExpansion) = 4.9.0-1 rpmlib(ScriptletInterpreterArgs) = 4.0.3-1 rpmlib(VersionedDependencies) = 3.0.3-1
Forresten er det utkastdokumentasjon for et RPMHåndbok På Fedora Prosjekt website. Jeg vet ikke da denne håndboken var produsert (sannsynlig 2003) men den seneste copyright notisen inkluderer 2010. Jeg kan ikke tale for resten av denne håndboken men kapitlene på Å programmering RPM med C og Å programmering RPM med Pyton Er åpenhjertig søppel og vill-lede direkte.
For eksempel liste opp 16-1 under viste (rpm1.c),, vil ikke en gang kompilere på Fedora 15 eller selv på Centos 5,6 som har en fjern eldre versjon av RPM.
#include <stdio.h>
#include <stdlib.h>
#include <rpmlib.h>
int
main(int argc, char * argv[])
{
int status = rpmReadConfigFiles( (const char*) NULL, (const char*) NULL);
if (status != 0) {
printf("Error reading RC files.\n");
exit(-1);
} else {
printf("Read RC OK\n");
}
rpmSetVerbosity(RPMMESS_NORMAL);
rpmShowRC( stdout );
exit(0);
}
Å listing er opp ikke en gang temmelig formatert i håndboken. Det over å formatere er all gruvedrift. RPMMESS_* definerer ble fjernet måte tilbake i 2007. Mens et til side, hvor har konvensjonen å bruke gått ut (-1) kommer fra ? Det ser mer lik noe fra Microsoft Vinduverden ! Kompilasjonene instruksjonene er også wierd.
$ cc -I/usr/include/rpm -o rpm1 rpm1.c -lrpm -lrpmdb -lrpmio –lpopt
Hvorfor behovet å henvise til lrpmdb og libpopt ? Ingen rutiner fra hvilket som helst av disse bibliotekene er brukt i den ovenfor koden.
Her er det ovenfor omarbeidte eksempelet å bearbeide Fedora 15 :
#include <stdio.h>
#include <stdlib.h>
#include <rpm/rpmlib.h>
#include <rpm/rpmlog.h>
int
main(int argc, char * argv[])
{
int status;
if ((status = rpmReadConfigFiles( (const char*) NULL, (const char*) NULL))) {
printf("ERROR: reading RC files\n");
exit(1);
}
rpmSetVerbosity(RPMLOG_NOTICE);
rpmShowRC(stdout);
exit(0);
}
Du kan kompilere den ovenfor kode bruken gcc -o rpm1 rpm1.c -lrpm -lrpmio.
Det følgende eksempelet viser måten at du tradisjonelt vil se er som blir brukt om å generere en liste av den installerte RPM pakkene på et system hvis du gjør en leting for slik kode på Internettet.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmdb.h>
int
main(int argc, char *argv[])
{
rpmdbMatchIterator mi;
int type, count;
char *name;
rpmdb db;
Header h;
rpmReadConfigFiles( NULL, NULL );
if (rpmdbOpen( "", &db, O_RDONLY, 0644 ) != 0) {
fprintf( stderr, "ERROR: Cannot open RPM database\n");
exit(1);
}
mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, NULL, 0);
while ((h = rpmdbNextIterator(mi))) {
headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count);
printf("%s\n", name);
}
rpmdbFreeIterator(mi);
rpmdbClose(db);
exit(0);
}
Det bruker rpmdbopen og rpmdbclose å åpne og å stenge RPM database, og en rpmdbmatchinterator å gjenta gjennom RPM database som leter etter passende adganger for RPMTAG_NAME.
Dette arbeider for Centos 5,6 men er ikke støttet i Fedora 15. I RPM 4.9.0 nesten fungerer all lav-nivå rpmdb manipulering samt rpmdbopen, rpmdbclose og rpmdbmatchinterator rutiner brukt over ble fjernet eller internalized. Se RPM 4.9.0 UtløserAnmerkninger For fulle detaljer.
Det følgende eksempelet bearbeider Fedora 15 og yter en liste av navnet (RPMTAG_NAME) og størrelse (RPMTAG_SIZE) av hver installerte pakke.
#include <stdio.h>
#include <stdlib.h>
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
int
main()
{
rpmts ts = NULL;
Header h;
rpmdbMatchIterator mi;
rpmtd td, tn;
char time_buffer[512];
int rc1, rc2;
td = rpmtdNew();
tn = rpmtdNew();
ts = rpmtsCreate();
rpmReadConfigFiles( NULL, NULL );
mi = rpmtsInitIterator( ts, RPMDBI_PACKAGES, NULL, 0);
while (NULL != (h = rpmdbNextIterator(mi))) {
h = headerLink(h);
rc1 = headerGet(h, RPMTAG_NAME, tn, HEADERGET_EXT);
rc2 = headerGet(h, RPMTAG_SIZE, td, HEADERGET_EXT);
// output installed package name and size
fprintf(stdout, "%s (%llu)\n", rpmtdGetString(tn), rpmtdGetNumber(td));
rpmtdReset(td);
rpmtdReset(tn);
headerFree(h);
}
rpmdbFreeIterator(mi);
rpmtsFree(ts);
exit(0);
}
Det følgende eksempelet viser seg å hvordan trykke mer informasjon om av de installerte pakkene på din system.
#include <stdio.h>
#include <stdlib.h>
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
int
main(int argc, char *argv[])
{
rpmts ts = NULL;
Header h;
rpmdbMatchIterator mi;
char *n, *v, *r, *g, *a;
ts = rpmtsCreate();
rpmReadConfigFiles( NULL, NULL );
mi = rpmtsInitIterator( ts, RPMDBI_PACKAGES, NULL, 0);
while (NULL != (h = rpmdbNextIterator(mi))) {
h = headerLink( h );
headerGetEntry( h, RPMTAG_NAME, NULL, (void**)&n, NULL);
headerGetEntry( h, RPMTAG_VERSION, NULL, (void**)&v, NULL);
headerGetEntry( h, RPMTAG_RELEASE, NULL, (void**)&r, NULL);
headerGetEntry( h, RPMTAG_GROUP, NULL, (void**)&g, NULL);
headerGetEntry( h, RPMTAG_ARCH, NULL, (void**)&a, NULL);
fprintf(stdout, "%s-%s-%s.%s\n", n, v, r, a);
headerFree(h);
}
rpmdbFreeIterator(mi);
rpmtsFree(ts);
exit(0);
}
Dette eksempelet bearbeider begge Centos 5,6 og Fedora 15. Her er noe prøveutgang :
iso-codes-0.53-1.noarch zlib-1.2.3-3.x86_64 libstdc++-4.1.2-50.el5.x86_64 db4-4.3.29-10.el5_5.2.x86_64 info-4.8-14.el5.x86_64 gawk-3.1.5-14.el5.x86_64 libgcrypt-1.4.4-5.el5.x86_64 libfontenc-1.0.2-2.2.el5.x86_64 libieee1284-0.2.9-4.el5.x86_64 grep-2.5.1-55.el5.x86_64 ....
Forresten har RPMPyton, Perl og Lua Støtte. Her er den tilsvarende koden som blir skrevet i Pyton :
#!/usr/bin/python
import rpm
ts=rpm.ts()
mi=ts.dbMatch()
for hdr in mi:
print "%s-%s-%s.%s" % (hdr['name'], hdr['version'], hdr['release'], hdr['arch'])
Mens du kan se Pyton svært kan forenkle ting når du ønsker å arbeide med RPM pakker.
Det følgende eksempelet demonstrerer hvordan å trykke flere merker i XML format for hver installerte pakke.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
struct tag {
int tagno;
char *element;
};
truct tag tags[] = {
{ RPMTAG_NAME, "Name"},
{ RPMTAG_VERSION, "Version" },
{ RPMTAG_RELEASE, "Release" },
{ RPMTAG_SUMMARY, "Summary" },
{ RPMTAG_BUILDTIME, "BuildTime"},
{ RPMTAG_BUILDHOST, "BuildHost" },
{ RPMTAG_INSTALLTIME, "InstallTime" },
{ RPMTAG_SIZE, "Size" },
{ RPMTAG_LICENSE, "License"},
{ RPMTAG_URL, "SourceUrl" },
{ RPMTAG_PAYLOADFORMAT, "PayloadFormat" },
{ RPMTAG_PAYLOADCOMPRESSOR, "PayloadCompressor" }
};
#define NTAGS (sizeof(tags)/sizeof(struct tag))
int
main(int argc, char *argv[])
{
rpmdbMatchIterator mi;
rpmts ts = NULL;
rpmtd td;
Header h;
int i;
td = rpmtdNew();
ts = rpmtsCreate();
rpmReadConfigFiles(NULL, NULL);
printf("<InstalledPackages>\n");
mi = rpmtsInitIterator( ts, RPMDBI_PACKAGES, NULL, 0);
while (NULL != (h = rpmdbNextIterator(mi))) {
h = headerLink(h);
printf(" <Package>\n");
for ( i = 0; i < NTAGS; i++) {
headerGet(h, (rpm_tag_t)tags[i].tagno, td, HEADERGET_ALLOC | HEADERGET_EXT);
if (td->data) {
switch(td->type) {
case RPM_NULL_TYPE:
break;
case RPM_CHAR_TYPE:
printf(" <%s>%s</%s>\n", tags[i].element, td->data, tags[i].element);
break;
case RPM_INT8_TYPE:
case RPM_INT16_TYPE:
printf(" <%s>%d</%s>\n", tags[i].element, td->data, tags[i].element);
break;
case RPM_INT32_TYPE:
if ((tags[i].tagno == RPMTAG_BUILDTIME) ||
(tags[i].tagno == RPMTAG_INSTALLTIME)) {
printf(" <%s>%s</%s>\n", tags[i].element, rpmtdFormat(td, RPMTD_FORMAT_DATE , NULL), tags[i].element);
} else if (tags[i].tagno == RPMTAG_SIZE) {
printf(" <%s>%" PRIu64 "<%s>\n", tags[i].element, rpmtdGetNumber(td), tags[i].element);
} else {
printf(" <%s>%u</%s>\n", tags[i].element, td->data, tags[i].element);
}
break;
case RPM_INT64_TYPE:
printf(" <%s>%" PRIu64 "</%s>\n", tags[i].element, rpmtdGetNumber(td), tags[i].element);
break;
case RPM_STRING_TYPE:
printf(" <%s>%s</%s>\n", tags[i].element, rpmtdGetString(td), tags[i].element);
break;
case RPM_BIN_TYPE:
printf(" <%s>%x</%s>\n", tags[i].element, td->data, tags[i].element);
break;
case RPM_STRING_ARRAY_TYPE:
default:
break;
}
}
rpmtdReset(td);
}
headerFree(h);
printf(" </Package>\n");
}
printf("</InstalledPackages>\n");
rpmdbFreeIterator(mi);
rpmtsFree(ts);
exit(0);
}
Dette bearbeider Fedora 15. Her er eksempelutgang :
<InstalledPackages>
<Package>
<Name>file-roller</Name>
<Version>3.0.2</Version>
<Release>1.fc15</Release>
<Summary>Tool for viewing and creating archives</Summary>
<BuildTime>Wed May 25 19:57:10 2011</BuildTime>
<BuildHost>x86-06.phx2.fedoraproject.org</BuildHost>
<InstallTime>Thu Jun 2 17:45:29 2011</InstallTime>
<Size>5928015<Size>
<License>GPLv2+</License>
<SourceUrl>http://download.gnome.org/sources/file-roller/</SourceUrl>
<PayloadFormat>cpio</PayloadFormat>
<PayloadCompressor>xz</PayloadCompressor>
</Package>
<Package>
<Name>expect</Name>
<Version>5.45</Version>
<Release>3.fc15</Release>
<Summary>A program-script interaction and testing utility</Summary>
<BuildTime>Wed Mar 16 09:58:49 2011</BuildTime>
<BuildHost>x86-12.phx2.fedoraproject.org</BuildHost>
<InstallTime>Mon Jun 13 10:24:25 2011</InstallTime>
<Size>559676<Size>
<License>Public Domain</License>
<SourceUrl>http://expect.nist.gov/</SourceUrl>
<PayloadFormat>cpio</PayloadFormat>
<PayloadCompressor>xz</PayloadCompressor>
</Package>
.....
<Package>
<Name>libxkbfile-devel</Name>
<Version>1.0.7</Version>
<Release>2.fc15</Release>
<Summary>X.Org X11 libxkbfile development package</Summary>
<BuildTime>Tue Feb 8 08:03:57 2011</BuildTime>
<BuildHost>x86-13.phx2.fedoraproject.org</BuildHost>
<InstallTime>Thu Jun 2 17:56:48 2011</InstallTime>
<Size>38055<Size>
<License>MIT</License>
<SourceUrl>http://www.x.org</SourceUrl>
<PayloadFormat>cpio</PayloadFormat>
<PayloadCompressor>xz</PayloadCompressor>
</Package>
</InstalledPackages>
Bemerk at versjonen av RPM på Centos 5,6 ikke støtter RPM_INT64_TYPE. Derfor daterer og er suchlike lagret seg i en RPM_INT32_TYPE.
Å vending nå til hvordan å undersøke enkel RPM pakker. Den følgende enkele eksempelet utstillingen du hvordan til forespørsel et pakkearkiv :
#include <stdio.h>
#include <stdlib.h>
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
#include <rpm/rpmlog.h>
int
main(int argc, char *argv[])
{
int i;
rpmts ts;
FD_t fd;
rpmRC rc;
Header hdr;
char *pkg_name, *pkg_version, *pkg_release;
rpmVSFlags vsflags = 0;
rc = rpmReadConfigFiles(NULL, NULL);
if (rc != RPMRC_OK) {
rpmlog(RPMLOG_NOTICE, "Unable to read RPM configuration.\n");
exit(1);
}
fd = Fopen(argv[1], "r.ufdio");
if ((!fd) || Ferror(fd)) {
rpmlog(RPMLOG_NOTICE, "Failed to open package file (%s)\n", Fstrerror(fd));
if (fd) {
Fclose(fd);
}
exit(1);
}
ts = rpmtsCreate();
vsflags |= _RPMVSF_NODIGESTS;
vsflags |= _RPMVSF_NOSIGNATURES;
vsflags |= RPMVSF_NOHDRCHK;
(void) rpmtsSetVSFlags(ts, vsflags);
rc = rpmReadPackageFile(ts, fd, argv[1], &hdr);
if (rc != RPMRC_OK) {
rpmlog(RPMLOG_NOTICE, "Could not read package file\n");
Fclose(fd);
exit(1);
}
Fclose(fd);
if (headerNVR(hdr, (const char **) &pkg_name,
(const char **) &pkg_version,
(const char **) &pkg_release))
{
rpmlog(RPMLOG_NOTICE, "Header read failed\n");
} else {
printf("Package is: %s-%s-%s\n", pkg_name, pkg_version, pkg_release);
headerFreeData(pkg_name, RPM_STRING_TYPE);
headerFreeData(pkg_version, RPM_STRING_TYPE);
headerFreeData(pkg_release, RPM_STRING_TYPE);
}
headerFree(hdr);
rpmtsFree(ts);
exit(0);
}
Du kan kompilere den ovenfor eksempel bruken gcc -o eksempel eksempel.c -lrpm -lrpmio. Dette kompilerer$uten feil eller varslinger på Centos 5,6 men utgir en varsling som headernvr er nedsatt på Fedora 15.
Anta deg vil finne ut listen av behovet en spesiell installert RPM pakke har. Enveiskjørt er å bruke rpm -qR
$ rpm -ql rpm-devel | /usr/lib/rpm/find-requires libacl.so.1()(64bit) libbz2.so.1()(64bit) libcap.so.2()(64bit) libc.so.6()(64bit) libc.so.6(GLIBC_2.14)(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3.4)(64bit) libdb-4.8.so()(64bit) libdl.so.2()(64bit) libelf.so.1()(64bit) liblua-5.1.so()(64bit) liblzma.so.5()(64bit) libm.so.6()(64bit) libnss3.so()(64bit) libpopt.so.0()(64bit) libpthread.so.0()(64bit) librpmio.so.2()(64bit) librpm.so.2()(64bit) librt.so.1()(64bit) libselinux.so.1()(64bit) libz.so.1()(64bit) $ rpm -ql rpm-devel | /usr/lib/rpm/rpmdeps -R /usr/bin/pkg-config libacl.so.1()(64bit) libbz2.so.1()(64bit) libc.so.6()(64bit) libc.so.6(GLIBC_2.14)(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3.4)(64bit) libcap.so.2()(64bit) libdb-4.8.so()(64bit) libdl.so.2()(64bit) libelf.so.1()(64bit) liblua-5.1.so()(64bit) liblzma.so.5()(64bit) libm.so.6()(64bit) libnss3.so()(64bit) libpopt.so.0()(64bit) libpopt.so.0(LIBPOPT_0)(64bit) libpthread.so.0()(64bit) librpm.so.2()(64bit) librpmbuild.so.2()(64bit) librpmio.so.2()(64bit) librpmsign.so.0()(64bit) librt.so.1()(64bit) libselinux.so.1()(64bit) libz.so.1()(64bit) rtld(GNU_HASH)
Her er et eksempel på et C program som vil trykke den tilsvarende informasjonen sammen med installerte arkiver, konflikter og foreldet :
#include <stdio.h>
#include <stdlib.h>
#include <rpm/rpmlib.h>
#include <rpm/rpmds.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
int
main(int argc, char *argv[])
{
const char *DNEVR;
rpmdbMatchIterator mi;
rpmds ds = NULL;
Header h;
rpmtd td_name, td_version, td_release, td_size, td_group, td_installtime;
rpmts ts = NULL;
rpmfi fi;
if (argc != 2) {
fprintf(stderr, "ERROR: No RPM specified on command line.\n");
exit(1);
}
td_name = rpmtdNew();
td_version = rpmtdNew();
td_release = rpmtdNew();
td_size = rpmtdNew();
td_group = rpmtdNew();
td_installtime = rpmtdNew();
ts = rpmtsCreate();
rpmReadConfigFiles(NULL, NULL);
mi = rpmtsInitIterator(ts, RPMTAG_NAME, argv[1], 0);
if (NULL != (h = rpmdbNextIterator(mi))) {
h = headerLink(h);
headerGet(h, RPMTAG_NAME, td_name, HEADERGET_EXT);
headerGet(h, RPMTAG_VERSION, td_version, HEADERGET_EXT);
headerGet(h, RPMTAG_RELEASE, td_release, HEADERGET_EXT);
headerGet(h, RPMTAG_SIZE, td_size, HEADERGET_EXT);
headerGet(h, RPMTAG_GROUP, td_group, HEADERGET_EXT);
headerGet(h, RPMTAG_INSTALLTIME, td_installtime, HEADERGET_EXT);
printf("%-20s: %s-%s-%s\n", "Package", rpmtdGetString(td_name), rpmtdGetString(td_version), rpmtdGetString(td_release));
printf("%-20s: %s\n", "Group", rpmtdGetString(td_group));
printf("%-20s: %llu\n", "Size", rpmtdGetNumber(td_size));
printf("%-20s: %s\n", "Installed on", rpmtdFormat(td_installtime, RPMTD_FORMAT_DATE, NULL));
fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
if (fi) {
fprintf(stdout, "\nFiles Provided:\n");
while (rpmfiNext(fi) != -1)
fprintf(stdout, " %s\n", rpmfiFN(fi));
fi = rpmfiFree(fi);
}
#if EXTRA
ds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
if (ds) {
fprintf(stdout, "\nProvides:\n");
while (rpmdsNext(ds) >= 0) {
if ((DNEVR = rpmdsDNEVR(ds)) != NULL)
fprintf(stdout, " %s\n", DNEVR + 1);
}
ds = rpmdsFree(ds);
}
#endif
ds = rpmdsNew(h, RPMTAG_REQUIRENAME, 0);
if (ds) {
fprintf(stdout, "\nRequires:\n");
while (rpmdsNext(ds) >= 0) {
if ((DNEVR = rpmdsDNEVR(ds)) != NULL)
fprintf(stdout, " %s\n", DNEVR + 1);
}
ds = rpmdsFree(ds);
}
ds = rpmdsNew(h, RPMTAG_OBSOLETENAME, 0);
if (ds) {
fprintf(stdout, "\nObsoletes:\n");
while (rpmdsNext(ds) >= 0) {
if ((DNEVR = rpmdsDNEVR(ds)) != NULL)
fprintf(stdout, " %s\n", DNEVR + 1);
}
ds = rpmdsFree(ds);
}
ds = rpmdsNew(h, RPMTAG_CONFLICTNAME, 0);
if (ds) {
fprintf(stdout, "\nConflicts:\n");
while (rpmdsNext(ds) >= 0) {
if ((DNEVR = rpmdsDNEVR(ds)) != NULL)
fprintf(stdout, " %s\n", DNEVR + 1);
}
ds = rpmdsFree(ds);
}
headerFree(h);
}
rpmdbFreeIterator(mi);
rpmtsFree(ts);
exit(0);
}
Dette bearbeider Fedora 15 men ikke på Centos 5,6. Imidlertid er det relativt lett å modifisere å få det å bearbeide Centos 5,6 og følgelig jeg vil forlate det som en øvelse for du. Hovedforandringene forholder seg til bruken av rpmtd typen. Disse må bli eliminert som versjonen av RPM på Centos 5,6 ikke støtter rpmtd typen.
Her er hva er ytet for xorg-x11-xkb-utils pakke :
./rpminfo.py xorg-x11-xkb-utils Package : xorg-x11-xkb-utils-7.5-3.fc15 Group : User Interface/X Size : 199629 Installed on : Thu Jun 2 17:39:31 2011 Files Provided: /usr/bin/setxkbmap /usr/bin/xkbcomp /usr/share/man/man1/setxkbmap.1.gz /usr/share/man/man1/xkbcomp.1.gz Requires: libX11.so.6()(64bit) libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3)(64bit) libc.so.6(GLIBC_2.3.4)(64bit) libc.so.6(GLIBC_2.4)(64bit) libc.so.6(GLIBC_2.7)(64bit) libxkbfile.so.1()(64bit) rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rtld(GNU_HASH) rpmlib(PayloadIsXz) <= 5.2-1 Obsoletes: XFree86 xorg-x11
Her er hvordan å gjøre den samme ting bruken Pytonen. Det er løst basert på eksempelet gitt ved listeen opp 17-3 i Fedora Prosjektet RPM Håndboken som forresten, ikke arbeider eller, frykter jeg, noensinne arbeidet ! Utgangen som er produsert ved denne skrifttypen er noe mer konstitusjonell enn som av C versjon selv om skrifttypen har færre ledninger av koden.
#!/usr/bin/python
import rpm, sys
def stringfromds(ds):
retlist=[]
for dataset in ds:
t=dataset[0]
values=t.split(" ")[1:]
retlist.append(" ".join(values))
return retlist
def printEntry(header, label, format, extra):
value = header.sprintf(format).strip()
print "%-20s: %s %s" % (label, value, extra)
def printHeader(h):
if h[rpm.RPMTAG_SOURCEPACKAGE]:
extra = " source package"
else:
extra = " binary package"
printEntry(h, 'Package', "%{NAME}-%{VERSION}-%{RELEASE}", extra)
printEntry(h, 'Group', "%{GROUP}", '')
printEntry(h, 'Summary', "%{Summary}", '')
printEntry(h, 'Arch-OS-Platform', "%{ARCH}-%{OS}-%{PLATFORM}", '')
printEntry(h, 'Vendor', "%{Vendor}", '')
printEntry(h, 'URL', "%{URL}", '')
printEntry(h, 'Size', "%{Size}", '')
printEntry(h, 'Installed on', "%{INSTALLTID:date}", '')
print "%-20s: %s" % ("Description", h['Description'])
print "\nFiles Provided:"
for fi in h.fiFromHeader():
print " ", fi[0], " ", fi[1], " ", fi[12]
ds = rpm.ds(h, 'requires')
if ds:
print "\nRequires:"
for d in stringfromds(ds):
print " ", d
ds = rpm.ds(h, 'obsoletes')
if ds:
print "\nObsoletes:"
for d in stringfromds(ds):
print " ", d
ds = rpm.ds(h, 'conflicts')
if ds:
print "\nConflicts:"
for d in stringfromds(ds):
print " ", d
def main(argv):
ts = rpm.TransactionSet()
for h in ts.dbMatch( 'name', argv[1]):
printHeader(h)
if __name__ == "__main__":
if len(sys.argv) == 1:
print "ERROR: No RPM specified on command line."
sys.exit(1)
else:
main(sys.argv)
Her er hva er ytet for xorg-x11-xkb-utils pakke :
$ ./rpminfo.py xorg-x11-xkb-utils Package : xorg-x11-xkb-utils-7.5-3.fc15 binary package Group : User Interface/X Summary : X.Org X11 xkb utilities Arch-OS-Platform : x86_64-linux-x86_64-redhat-linux-gnu Vendor : Fedora Project URL : http://www.x.org Size : 199629 Installed on : Thu Jun 2 17:38:11 2011 Description : X.Org X11 xkb core utilities Files Provided: /usr/bin/setxkbmap 19224 e5e5757d15ca331474c43a0319d0307920fb17b3ed9d04c041e13b56c4aa08e4 /usr/bin/xkbcomp 176960 e6b265d05cd6432859d7a24fb8f6d6ccec3cbd668abd54afd9606322d3dd9a9e /usr/share/man/man1/setxkbmap.1.gz 1753 4a91c43a425699e4209880cf80a28eeae565763ece6cffaa8308a92139fd3250 /usr/share/man/man1/xkbcomp.1.gz 1692 29d16f6c864bf62b7f3e3e0400e121def3f85e6d64676dffb6d92c54503a0c76 Requires: libX11.so.6()(64bit) libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3)(64bit) libc.so.6(GLIBC_2.3.4)(64bit) libc.so.6(GLIBC_2.4)(64bit) libc.so.6(GLIBC_2.7)(64bit) libxkbfile.so.1()(64bit) rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rtld(GNU_HASH) rpmlib(PayloadIsXz) <= 5.2-1 Obsoletes: XFree86 xorg-x11
En gang til kan du se det er fjernt lettere å bruke Pyton enn C når arbeide med RPM pakke innvendig.
Før jeg mønstrer på på denne posten, må jeg si at Linux verden ikke trenger to forskjellige utviklingsfellesskap omkring RPM. Ja hadde konkurranse mellom den to RPM utviklingsfellesskap ledet til noen forbedringer i RPM Pakken Sjefen men det har også ledet godt kjent dokumentert APIs slik som rpmdbopen og rpmdbclose blir fjernet i nylige utløsninger. Disse to fellesskapene burde begrave øksen og burde smelte sammen deres innsatser. Av og til er forking fellesskapprosjekter gode og bly til virkelig nyttig nyskapning. I dette tilfellen er det min mening at det ikke er !
Jeg må også si at dokumentasjonen for brukeen RPMS bibliotek APIs er åpenhjertig grusom og begge RPM fremkallerfellesskap synes ikke å lage noen alvorlig forsøk å informere brukere av deres biblioteker av hovedforandringene i APIs foruten uklare referanser til forandringene i deres respektive utløseranmerkninger. Dette må bli rettet. Et trinn i den riktige retningen ville være å gi enkle eksempeler på API praksis når en ny API er introdusert eller en gammel API fjernet.
Nyt !


























