15.5. Сравнение rpm-файла и установленного пакета

Сведя вместе знания о приемах работы с rpm-файлами и БД RPM можно создать немало полезных программ. Код утилиты для сравнения файла rpm-пакета и установленного пакета показан ниже - программа vercompare.c.

/* Compares a package file with an installed package,

telling which one is newer.

Usage:

vercompare pkg_files+

Compile as

cc -I/usr/include/rpm -o vercompare vercompare.c -lrpm -lrpmdb -lrpmio -lpopt
*/

#include <stdlib.h>

#include <rpmcli.h>

#include <rpmdb.h>

#include <rpmds.h>

#include <rpmts.h>

/* Set up a table of options using standard RPM options. */

static struct poptOption optionsTable[] = {

{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
"Common options for all rpm modes and executables:",
NULL },

POPT_AUTOALIAS

POPT_AUTOHELP

POPT_TABLEEND

};

int main(int argc, char * argv[])

{

poptContext context;

const char ** fnp;

rpmdbMatchIterator iter;

Header file_header, installed_header;

rpmts ts;

rpmds dependency_set;

FD_t fd;

rpmRC rpmrc;

int rc;

context = rpmcliInit(argc, argv, optionsTable);

if (context == NULL) {

exit(EXIT_FAILURE);

}

ts = rpmtsCreate();

for (fnp = poptGetArgs(context); fnp && *fnp; fnp++) {

/* Read package header, continuing to next arg on failure. */

fd = Fopen(*fnp, "r.ufdio");

if (fd == NULL || Ferror(fd)) {
rpmError(RPMERR_OPEN, "open of %s failed: %s\n", *fnp,
Fstrerror(fd));

if (fd) {

Fclose(fd);

}

continue;

}

rpmrc = rpmReadPackageFile(ts, fd, *fnp, &file_header);

Fclose(fd);

if (rpmrc != RPMRC_OK) {
rpmError(RPMERR_OPEN, "%s cannot be read\n", *fnp);

continue;

}

/* Generate "name <= epoch:version-release" depset for package */

dependency_set = rpmdsThis(file_header, RPMTAG_REQUIRENAME,
(RPMSENSE_EQUAL|RPMSENSE_LESS));

rc = -1; /* assume no package is installed. */

/* Search all installed packages with same name. */

iter = rpmtsInitIterator(ts, RPMTAG_NAME, rpmdsN(dependency_set), 0);

while ((installed_header = rpmdbNextIterator(iter)) != NULL) {

/* Is the installed package newer than the file? */

rc = rpmdsNVRMatchesDep(installed_header, dependency_set, 1);

switch (rc) {

case 1:

if ( rpmIsVerbose() )

fprintf(stderr, "installed package is older (or same) as %s\n",
*fnp);

break;

case 0:

if ( rpmIsVerbose() )

fprintf(stderr, "installed package is newer than %s\n",
*fnp);

break;

}

}

/* Clean up. */

iter = rpmdbFreeIterator(iter);

dependency_set = rpmdsFree(dependency_set);

if (rc < 0 && rpmIsVerbose() )

fprintf(stderr, "no package is installed %s\n", *fnp);

}

ts = rpmtsFree(ts);

context = rpmcliFini(context);

return rc;

}

Программа vercompare.c выполняет чтение из файла rpm-пакета и поиск в БД RPM. В коде вводятся сеты транзакций и сеты зависимости. Используйте пример для создания собственных RPM программ.

Для запуска программы ей надо передать в качестве параметров имена одного или нескольких rpm-файлов. Программа извлечет имена пакетов из файлов и запросит БД RPM о пакетах с такими именами. Для каждого такого пакета vercompare даст заключение, является ли данный пакет старее установленного, идентичным установленному или новее установленного.

Например, если установлен пакет jikes-1.17-1 и имеется файл пакета более новой версии, тогда при запуске программы увидим примерно следующее:

$ ./vercompare -v jikes-1.18-1.i386.rpm

installed package is older (or same) as jikes-1.18-1.i386.rpm

Если происходит сравнение с более старой версией rpm-файла, получится такой результат:

$ ./vercompare -v jikes-1.14-1-glibc-2.2.i386.rpm

installed package is newer than jikes-1.14-1-glibc-2.2.i386.rpm

И, наконец, если установленный пакет имеет такую же версию, получим следующее:

$ ./vercompare -v jikes-1.17-glibc2.2-1.i386.rpm

installed package is older (or same) as jikes-1.17-glibc2.2-1.i386.rpm

Этот аспект сравнения можно изменить путем передачи флагов в rpmdsThis.

Если нужно организовать многословный вывод, используем -v.

Функции интерфейса командной строки RPM используют те же общие опции, что и утилиты rpm и rpmbuild. Вы можете использовать эти функции для высокоуровневого доступа к RPM. Например, для активации опции запроса используйте rpmcliQuery:

int rpmcliQuery(rpmts transaction_set,
QVA_t qva,
const char **argv);

Установите переменную QVA_t так, чтобы она указывала на глобальную переменную rpmQVKArgs, которая инициализирована из глобальной таблицы опций для режима запросов, rpmQueryPoptTable. Передайте rpmcliQuery список имен файлов или имен пакетов.

Для поддержки опций запросов необходимо иметь записи в локальной таблице опций poptOption. Для доступа к этим опциям добавьте следующую запись:

{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmQueryPoptTable, 0,
"Query options (with -q or --query):",
NULL },

С опциями pmQueryPoptTable можно создать программу, которая будет работать как команда pm --query, используя следующий код:

poptContext context;

QVA_t qva = &rpmQVKArgs;

rpmts ts;

int ec;

context = rpmcliInit(argc, argv, optionsTable);

if (context == NULL) {

/* Display error and exit... */

}

ts = rpmtsCreate();

if (qva->qva_mode == 'q') {

/* Make sure there's something to do. */

if (qva->qva_source != RPMQV_ALL && !poptPeekArg(context)) {
fprintf(stderr, "no arguments given for --query");

exit(EXIT_FAILURE);

}

ec = rpmcliQuery(ts, qva, (const char **) poptGetArgs(context));

}

ts = rpmtsFree(ts);

context = rpmcliFini(context);

Этот код поддерживает все опции запросов, что и команда rpm. Это одновременно и хорошо и плохо. Если вы хотите выполнять в точности, что и утилита rpm, можно с тем же успехом использовать саму утилиту. Но если вы хотите использовать поддержку запросов из программы, возможно есть более простой способ сделать это.

С помощью небольшого куска кода можно добавить в программу функционал, обеспечивающийся опцией --verify. Для этого необходимо включить определения командной строки из глобальной таблицы rpmVerifyPoptTable:

/* Add in --verify options. */

{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmVerifyPoptTable, 0,
"Verify options (with -V or --verify):",
NULL },

Далее можно проверить режим верификации и поддержку опций с помощью кода, подобного этому:

if (qva->qva_mode == 'V') {

rpmVerifyFlags verifyFlags = VERIFY_ALL;

/* Verify flags are negated from query flags. */

verifyFlags &= ~qva->qva_flags;

qva->qva_flags = (rpmQueryFlags) verifyFlags;

/* Make sure there's something to do. */

if (qva->qva_source != RPMQV_ALL && !poptPeekArg(context)) {

fprintf(stderr, "no arguments given for --verify");

exit(EXIT_FAILURE);

}

ec = rpmcliVerify(ts, qva, (const char **)
poptGetArgs(context));

}

Рабочая лошадь этого кода - высокоуровневая функция rpmcliVerify, она выполняет всю ту работу, которую выполняет утилита rpm под опцией --verify.

int rpmcliVerify(rpmts transaction_set,
QVA_t qva,

const char **argv);

И вновь, установите переменную QVA_t чтобы она указывала на глобальную переменную rpmQVKArgs, которая инициализирована значениями из глобальной таблицы опций rpmQueryPoptTable.

Соберем это все вместе и получим программу rpmq.c, которая выполняет все то же, что и команды rpm --query и rpm --verify:

/*
rpm --query and --verify modes in standalone program.
Compile as
cc -I/usr/include/rpm -o rpmq rpmq.c -lrpm -lrpmdb -lrpmio -lpopt
See option usage by invoking
./rpmq --help
*/

#include <stdlib.h>

#include <rpmcli.h>

#include <rpmdb.h>

#include <rpmds.h>

#include <rpmts.h>

/* Set up a table of options. */

static struct poptOption optionsTable[] = {
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
"Common options for all rpm modes and executables:",
NULL },

{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmQueryPoptTable, 0,
"Query options (with -q or --query):",
NULL },

/* Add in --verify options. */

{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmVerifyPoptTable, 0,
"Verify options (with -V or --verify):",
NULL },

POPT_AUTOALIAS

POPT_AUTOHELP

POPT_TABLEEND

};

int main(int argc, char * argv[])

{

poptContext context;

QVA_t qva = &rpmQVKArgs;

rpmts ts;

int ec;

context = rpmcliInit(argc, argv, optionsTable);

if (context == NULL) {
poptPrintUsage(context, stderr, 0);

exit(EXIT_FAILURE);

}

ts = rpmtsCreate();

/* Check for query mode. */

if (qva->qva_mode == 'q') {

/* Make sure there's something to do. */

if (qva->qva_source != RPMQV_ALL && !poptPeekArg(context)) {
fprintf(stderr, "no arguments given for --query");

exit(EXIT_FAILURE);

}

ec = rpmcliQuery(ts, qva, (const char **) poptGetArgs(context));

}

/* Check for verify mode. */

else if (qva->qva_mode == 'V') {
rpmVerifyFlags verifyFlags = VERIFY_ALL;

/* Verify flags are negated from query flags. */

verifyFlags &= ~qva->qva_flags;

qva->qva_flags = (rpmQueryFlags) verifyFlags;

/* Make sure there's something to do. */

if (qva->qva_source != RPMQV_ALL && !poptPeekArg(context)) {
fprintf(stderr, "no arguments given for --verify");

exit(EXIT_FAILURE);

}

ec = rpmcliVerify(ts, qva, (const char **) poptGetArgs(context));

}

else {

poptPrintUsage(context, stderr, 0);

exit(EXIT_FAILURE);

}

ts = rpmtsFree(ts);

context = rpmcliFini(context);

return ec;

}

Для такой функциональности не так уж много кода, не так ли? Эта эффективность достигается благодаря использованию высокоуровневых функций для доступа к интерфейсу командной строки.

Программа rpmq выполняет все те же задачи, что и команды rpm --query и rpm --verify. Например, поддерживаются форматы расширенных запросов:

$ ./rpmq -q --qf "%{NAME} %{INSTALLTID:date}\n" jikes

jikes Fri 25 Oct 2002 06:49:38 PM CDT

Далее - Раздел 16. Программирование RPM на Python
Назад - Сет зависимости
Содержание