Recentemente, I necessário para recuperar detalhes sobre os pacotes de software instalados em Fedora 15, Red Hat Enterprise Linux, CentOS e outras distribuições que distribuem seus pacotes de software usando o RPM empacotam o gerente. A minha surpresa, o que deve ter sido uma tarefa relativamente simples despejada ser completamente desarrumado por causa das mudanças nos APIs da biblioteca do RPM e do formato interno sobre os últimos anos. Neste borne, eu demonstro como recuperar a informação sobre pacotes do RPM usando C e pitão.
O RPM é uma comando-linha ou um sistema de gestão conduzido API do pacote capaz de instalar, de desinstalar, de verific, de perguntar, e de actualizar pacotes de software do linux ou do Unix. Cada pacote de software consiste em um arquivo das limas junto com a informação sobre o pacote tal como seu número de versão, um sumário e uma descrição, e informação da dependência. Há igualmente uma biblioteca API para permitir colaboradores de controlar tais transações dos linguagens de programação compilados tais como C ou línguas scripting tal pitão. As limas do pacote são escritas ao disco na ordem do byte da rede. Caso necessário, o RPM converte automaticamente dados à ordem do byte do anfitrião quando a lima do pacote é lida.
O RPM foi desenvolvido inicialmente em 1997 por Erik Troan e por Marc Ewing para o uso na distribuição de Red Hat Linux. Por muitos anos era um projeto do opensource que não recebesse muita amor ou atenção. Esse retrato mudou em 2007 adiantado em que as duas (e competindo) Comunidades para o Desenvolvimento separadas foram lanç.
A Comunidade para o Desenvolvimento mais proeminente do RPM é rpm.org que é conduzida por Red Hat. De acordo com seu Web site:
Depois que uma ruptura longa rpm.org do desenvolvimento foi relançada em 2007 com o objetivo para recuperar a posição como rio acima para casa do RPM. Os remendos que tinham empilhado acima nas distribuições diferentes foram integrados em primeiro na base do código tão distante quanto possível. Nós queremos o RPM não ser a província de uma companhia, ou um jogo pequeno dos colaboradores. Precisa de ser desenvolvida em uma comunidade aberta, de ser consumida e contribuído por muitos companhias, usuários, distribuições, e colaboradores. Nós damos boas-vindas conseqüentemente a alguns e a todos os contribuinte.
….
O RPM permanecerá para trás - compatível a 4.4.2 por um tempo completamente longo. É essencial que os pacotes do terceiro podem ser instalados sem a necessidade de recompile os - especial para distribuições da empresa.
Em maio 2007 Red Hat empregou Panu Matilainen para trabalhar no projeto do RPM. A primeira revisão principal do código realizava-se em julho 2007; a versão 4.8 foram liberados em janeiro 2010, e 4.9 em março 2011. Esta versão é usada por distribuições tais como Fedora, Red Hat Enterprise Linux, o openSUSE, a empresa do linux de SUSE e o CentOS.
A outra Comunidade para o Desenvolvimento do RPM é rpm5.org que é ligação por Jeff Johnson que era um mantedor do RPM enquanto um empregado de Red Hat. A versão 5.0 do RPM foi liberada em maio 2007. Sua versão mais atrasada é 5.3.11 02-Jun-2011 datado. Esta versão do RPM é usada por distribuições tais como o linux da unidade e o linux dos cAos, e igualmente pelo projeto de OpenPKG que fornece pacotes para alguns outro Unix-como plataformas. Aparentemente Mandriva comutou-lhe recentemente igualmente embora parecesse haver alguma controvérsia sobre essa decisão particular.
O formato de um pacote do RPM é binário e consiste em três seções na seguinte ordem:
- A seção da ligação identifica a lima como uma lima do RPM. Contem um número de encabeçamentos obsoletos que em versões anteriores precedentes do RPM foram usados para armazenar a informação usada internamente pelo RPM. Hoje, entretanto, a única finalidade da seção da ligação é fazê-la fácil identificar uma lima do pacote do RPM.
- A seção da assinatura contem a informação que pode ser usada para verific a integridade, e opcionalmente, a autenticidade da maioria do pacote. Esta seção é executada usando uma estrutura do encabeçamento (veja abaixo).
- A seção do encabeçamento contem os metadata do pacote tais como o nome, versão, arquitetura, lista de limas incluídas e suchlike. É executada demasiado como uma estrutura do encabeçamento.
- A seção final do Th contem o arquivo real da lima, que está geralmente no formato do cpio, comprimido com gzip mas versões mais recentes do RPM pode igualmente usar bzip2, lzma ou a compressão do xz e xar (arquivo de XML) são suportados por RPM 5.0.
É aqui o que a ligação contem. Não use qualquer coisa da ligação exceto o número e o tipo principais da assinatura.
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! */
};
O conceito da estrutura do encabeçamento é solução do RPM ao problema facilmente de manipular a informação em uma maneira estandardizada. A finalidade de uma estrutura do encabeçamento é conter zero ou mais partes de dados. Há três seções a cada estrutura do encabeçamento. A primeira seção é sabida como o encabeçamento da estrutura do encabeçamento. O encabeçamento da estrutura do encabeçamento é usado para identificar o começo de uma estrutura do encabeçamento, de seu tamanho, e do número de artigos que de dados contem. Depois da estrutura do encabeçamento o encabeçamento é uma área chamada o índice.
O índice da estrutura do encabeçamento é compo de zero ou mais entradas de índice. Cada entrada é dezesseis bytes por muito tempo. Os primeiros quatro bytes contêm um Tag - um valor numérico que identifique que tipo de dados é aguçado pela entrada. Há um grande número Tag do encabeçamento definidos em rpmtag.h. Estão aqui alguns deles:
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;
Depois de cada Tag, é um tipo four-byte, que seja um valor numérico que descreva o formato dos dados aguçado pela entrada. Está aqui a lista atual de tipos definidos em 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;
A maioria destes tipos devem ser evidentes. A diferença entre um tipo da CORDA e um tipo de STRING_ARRAY é que o anterior é uma corda terminada nula regular visto que o último é uma coleção das cordas. RPM_I18NSTRING_TYPE é menosprezado.
Está em seguida um valor deslocado de 4 bytes que contenha a posição real dos dados, relativo ao começo da loja. Finalmente, há uma contagem four-byte que contenha o número de artigos de dados aguçado pela entrada de índice. Os dados da CORDA têm sempre uma contagem de 1, quando os dados de STRING_ARRAY tiverem uma contagem igual ao número de cordas contidas na loja.
Depois que o índice vem a loja. É na loja que os artigos de dados reais estão mantidos. Os dados na loja são embalados junto tão pròxima como possível na ordem do byte da rede, isto é a maioria de byte significativo primeiramente. Os dados da CORDA são terminados com um byte nulo. Os dados do inteiro são armazenados no limite natural para seu tipo, isto é um inteiro de 32 bits é armazenado em um limite de byte 4.
Aprovação, hora de falar sobre como alcançar programmatically pacotes do RPM. Porém antes que você começar usar a biblioteca do RPM, você precisa de figurar para fora que versão da biblioteca do RPM você está usando. Se você está escrevendo um certificado de escudo, este é fácil de fazer. Por exemplo em Fedora 15:
$ /usr/lib/rpm/rpmdeps --version RPM version 4.9.0
A biblioteca do RPM de rpm5.org tem o rpmlibVersion API que pode ser usado para recuperar a versão da biblioteca ou você pode simplesmente alcançar a corda de RPMVERSION como mostrado abaixo.
#include <stdio.h>
#include <rpm/rpmlib.h>
int
main()
{
fprintf(stderr, "RPM Version: %s\n", RPMVERSION);
}
Infelizmente a biblioteca do RPM de rpm.org não tem qualquer coisa similar. Anote aos colaboradores de rpm.org! Adicione por favor o rpmlibVersion API a sua lista de APIs públicos suportados de modo que as aplicações que precisam de alcançar programmatically pacotes do RPM e as bases de dados podem facilmente figurar para fora que flavor e a liberação de uma biblioteca que do RPM estão tratando e para ajustar conformemente seu código.
Em Fedora 15, a corda da versão que é retornada é 4.9.0 que indica uma versão do RPM que foi liberado em março 2011. Em CentOS 5.6, a corda da versão que é retornada é 4.4.2.3 que indica uma versão significativamente mais velha do RPM liberado em abril 2008. Há umas diferenças significativas e principais entre estas versões. Em particular o código de C escrito para uma destas duas versões particulares do RPM provavelmente não trabalhará para a outra versão sem modificação.
Começ mais mau, pela maneira, porque Fedora 15 e Red Hat Enterprise Linux 6 usam a biblioteca mais nova do RPM que igualmente inclui um formato novo e a mistura de SHA. Isto causa problemas quando você tenta instalar um RPM construído em uma destas plataformas em uma plataforma mais velha tal como CentOS 5.6 que não sabe sobre o formato e a mistura novos. Em conseqüência, CentOS 5.6 queixar-se-á que não pode confirmar a integridade do pacote do RPM.
Você igualmente precisa de poder determinar que características uma versão particular do RPM suporta. Está aqui o one-way de fazê-la:
#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);
}
Você tem que ter o pacote do desenvolvimento do RPM (RPM-devel) instalado a fim poder compilar o código acima.
Este código imprime simplesmente - para fora o jogo suportado da característica do RPM. É aqui o que outputted para a biblioteca do RPM em 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
Pela maneira, há uma documentação do esboço para um guia do RPM no Web site do projeto de Fedora. Eu não sei quando este guia fui produzido (provavelmente 2003) mas a observação de direitos reservados a mais atrasada inclui 2010. Eu não posso falar para o descanso deste guia mas os capítulos em programar o RPM com C e em programar o RPM com pitão são sincera desperdícios e engano extremoso.
Por exemplo, alistando 16-1 (rpm1.c), mostrado abaixo, não compilarão mesmo em Fedora 15 ou mesmo em Centos 5.6 qual tem uma versão distante mais velha do 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);
}
A lista não é mesmo bonita formatada no guia. O formato acima é toda a mina. O RPMMESS_* define era maneira removida para trás em 2007. Como um aparte, de onde o uso da convenção retirou (- 1) vindo? Esse olha mais como algo do mundo de Microsoft Windows! As instruções da compilação são igualmente estranhas.
$ cc -I/usr/include/rpm -o rpm1 rpm1.c -lrpm -lrpmdb -lrpmio –lpopt
Por que a necessidade de prover o lrpmdb e o libpopt? Nenhuma rotina de tampouco destas bibliotecas é usada no código acima.
Está aqui o exemplo acima reescrito para trabalhar em 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);
}
Você pode compilar o código acima usando GCC - o rpm1 rpm1.c - lrpm - lrpmio.
O seguinte exemplo mostra a maneira que você verá tradicional sendo usado para gerar uma lista dos pacotes instalados do RPM em um sistema se você faz uma busca para tal código no Internet.
#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);
}
Usa-se rpmdbOpen e rpmdbClose para abrir e fechar a base de dados do RPM, e um rpmdbMatchInterator para iterar através da base de dados do RPM que procura entradas de harmonização para RPMTAG_NAME.
Isto trabalha para CentOS 5.6 mas não é suportado em Fedora 15. Em RPM 4.9.0, quase todas as funções de baixo nível da manipulação do rpmdb que incluem as rotinas do rpmdbOpen, do rpmdbClose e do rpmdbMatchInterator usadas acima foram removidas ou interiorizadas. Veja as notas de liberação do RPM 4.9.0 para detalhes cheios.
O seguinte exemplo trabalha em Fedora 15 e outputs uma lista do nome (RPMTAG_NAME) e do tamanho (RPMTAG_SIZE) de cada pacote instalado.
#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);
}
O seguinte exemplo mostra como imprimir para fora mais informação sobre dos pacotes instalados em seu sistema.
#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);
}
Este exemplo trabalha em CentOS 5.6 e em Fedora 15. Está aqui alguma saída de amostra:
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 ....
Pela maneira, o RPM tem a sustentação do pitão, do Perl e do Lua. Está aqui o código equivalente escrito no pitão:
#!/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'])
Como você pode ver o pitão pode extremamente simplificar coisas quando você deseja trabalhar com pacotes do RPM.
O seguinte exemplo demonstra como imprimir para fora um número de Tag no formato de XML para cada pacote instalado.
#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);
}
Isto trabalha em Fedora 15. Está aqui a saída de exemplo:
<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>
Anote que a versão do RPM em CentOS 5.6 não suporta RPM_INT64_TYPE. Em conseqüência data e suchlike são armazenados em um RPM_INT32_TYPE.
Giro agora para como examinar pacotes individuais do RPM. A seguinte mostra do exemplo simples você como perguntar uma lima do pacote:
#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);
}
Você pode compilar o exemplo acima usando GCC - exemplo example.c do - lrpm - lrpmio. Isto compila sem erros ou avisos em CentOS 5.6 mas emite um aviso que o headerNVR está menosprezado em Fedora 15.
Supor você quer encontrar que a lista de exigências que um pacote instalado detalhe do RPM tem. O one-way é usar RPM - qR É aqui o que outputted para o pacote de xorg-x11-xkb-utils: Antes que eu assine fora neste borne, eu tenho que dizer que o mundo do linux não precisa as duas Comunidades para o Desenvolvimento diferentes em torno do RPM. Sim, a competição entre as duas Comunidades para o Desenvolvimento do RPM teve conduzir a algumas melhorias no gerente do pacote do RPM mas igualmente tem conduzir aos APIs documentados conhecidos como rpmdbOpen e rpmdbClose que está sendo removido em liberações recentes. Estas duas comunidades devem enterrar o machado e fundir seus esforços. Que bifurca às vezes a comunidade projetos é boa e conduz à inovação realmente útil. Neste caso é minha opinião que não é! Eu igualmente tenho que dizer que a documentação para usar os APIs da biblioteca do RPM é sincera atroz e ambas as comunidades do colaborador do RPM parecem não fazer nenhuma tentativa séria de informar usuários de suas bibliotecas das mudanças importantes nos APIs diferentes das referências obscuras às mudanças em suas notas de liberação respectivas. Isto precisa de ser corrigido. Uma etapa no sentido correto seria fornecer os exemplos simples do uso do API quando um API novo é introduzido ou de um API velho removidos. Aprecie!
$ 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)
Está aqui um exemplo do programa da A.A. que imprima para fora a informação equivalente junto com limas instaladas, conflitos e obsoletes:
#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);
}
Isto trabalha em Fedora 15 mas não em CentOS 5.6. Entretanto, é relativamente fácil modificar para consegui-lo trabalhar em CentOS 5.6 e assim eu sairei que como um exercício para você. As mudanças principais relacionam-se ao uso do tipo do rpmtd. Estes precisam de ser eliminados como a versão do RPM em CentOS 5.6 não suportam o tipo do rpmtd.
./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
É aqui como fazer a mesma coisa usando o pitão. É baseado frouxamente no exemplo dado na lista 17-3 no guia do projeto RPM de Fedora que pela maneira, não trabalha nem, mim suspeita, trabalhado nunca! A saída produziu por este certificado é um tanto mais detalhada do que aquela da versão de C mesmo que o certificado tivesse poucas linhas de código.
#!/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)
É aqui o que outputted para o pacote de xorg-x11-xkb-utils:
$ ./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
Mais uma vez, você pode ver que é distante mais fácil usar o pitão do que C ao trabalhar com os internals do pacote do RPM.


























