1170 lines
31 KiB
C
1170 lines
31 KiB
C
/** \ingroup python
|
|
* \file python/rpmmodule.c
|
|
*/
|
|
|
|
#include <alloca.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <glob.h> /* XXX rpmio.h */
|
|
#include <dirent.h> /* XXX rpmio.h */
|
|
#include <locale.h>
|
|
#include <time.h>
|
|
|
|
#include "Python.h"
|
|
#include "rpmio_internal.h"
|
|
#include "rpmcli.h" /* XXX for rpmCheckSig */
|
|
#include "misc.h"
|
|
#include "header_internal.h"
|
|
#include "upgrade.h"
|
|
|
|
#include "db-py.h"
|
|
#include "header-py.h"
|
|
|
|
extern int _rpmio_debug;
|
|
|
|
#ifdef __LCLINT__
|
|
#undef PyObject_HEAD
|
|
#define PyObject_HEAD int _PyObjectHead
|
|
#endif
|
|
|
|
extern int mdfile(const char *fn, unsigned char *digest);
|
|
|
|
void initrpm(void);
|
|
|
|
/* from lib/misc.c */
|
|
int rpmvercmp(const char * one, const char * two);
|
|
|
|
/** \ingroup python
|
|
*/
|
|
typedef struct rpmtransObject_s rpmtransObject;
|
|
|
|
/** \ingroup python
|
|
* \class header
|
|
* \brief A python header object represents an RPM package header.
|
|
*
|
|
* All RPM packages have headers that provide metadata for the package.
|
|
* Header objects can be returned by database queries or loaded from a
|
|
* binary package on disk.
|
|
*
|
|
* The headerFromPackage function loads the package header from a
|
|
* package on disk. It returns a tuple of a "isSource" flag and the
|
|
* header object. The "isSource" flag is set to 1 if the package
|
|
* header was read from a source rpm or to 0 if the package header was
|
|
* read from a binary rpm.
|
|
*
|
|
* For example:
|
|
* \code
|
|
* import os, rpm
|
|
*
|
|
* fd = os.open("/tmp/foo-1.0-1.i386.rpm", os.O_RDONLY)
|
|
* (header, isSource) = rpm.headerFromPackage(fd)
|
|
* fd.close()
|
|
* \endcode
|
|
* The Python interface to the header data is quite elegant. It
|
|
* presents the data in a dictionary form. We'll take the header we
|
|
* just loaded and access the data within it:
|
|
* \code
|
|
* print header[rpm.RPMTAG_NAME]
|
|
* print header[rpm.RPMTAG_VERSION]
|
|
* print header[rpm.RPMTAG_RELEASE]
|
|
* \endcode
|
|
* in the case of our "foor-1.0-1.i386.rpm" package, this code would
|
|
* output:
|
|
\verbatim
|
|
foo
|
|
1.0
|
|
1
|
|
\endverbatim
|
|
* You make also access the header data by string name:
|
|
* \code
|
|
* print header['name']
|
|
* \endcode
|
|
* This method of access is a bit slower because the name must be
|
|
* translated into the tag number dynamically. You also must make sure
|
|
* the strings in header lookups don't get translated, or the lookups
|
|
* will fail.
|
|
*/
|
|
|
|
/** \ingroup python
|
|
* \name Class: rpmtrans
|
|
* \class rpmtrans
|
|
* \brief A python rpmtrans object represents an RPM transaction set.
|
|
*
|
|
* The transaction set is the workhorse of RPM. It performs the
|
|
* installation and upgrade of packages. The rpmtrans object is
|
|
* instantiated by the TransactionSet function in the rpm module.
|
|
*
|
|
* The TransactionSet function takes two optional arguments. The first
|
|
* argument is the root path, the second is an open database to perform
|
|
* the transaction set upon.
|
|
*
|
|
* A rpmtrans object has the following methods:
|
|
*
|
|
* - add(header,data,mode) Add a binary package to a transaction set.
|
|
* @param header the header to be added
|
|
* @param data user data that will be passed to the transaction callback
|
|
* during transaction execution
|
|
* @param mode optional argument that specifies if this package should
|
|
* be installed ('i'), upgraded ('u'), or if it is just
|
|
* available to the transaction when computing
|
|
* dependencies but no action should be performed with it
|
|
* ('a').
|
|
*
|
|
* - remove
|
|
*
|
|
* - depcheck() Perform a dependency and conflict check on the
|
|
* transaction set. After headers have been added to a
|
|
* transaction set, a dependency check can be performed
|
|
* to make sure that all package dependencies are
|
|
* satisfied.
|
|
* @return None If there are no unresolved dependencies
|
|
* Otherwise a list of complex tuples is returned, one tuple per
|
|
* unresolved dependency, with
|
|
* The format of the dependency tuple is:
|
|
* ((packageName, packageVersion, packageRelease),
|
|
* (reqName, reqVersion),
|
|
* needsFlags,
|
|
* suggestedPackage,
|
|
* sense)
|
|
* packageName, packageVersion, packageRelease are the name,
|
|
* version, and release of the package that has the unresolved
|
|
* dependency or conflict.
|
|
* The reqName and reqVersion are the name and version of the
|
|
* requirement or conflict.
|
|
* The needsFlags is a bitfield that describes the versioned
|
|
* nature of a requirement or conflict. The constants
|
|
* rpm.RPMDEP_SENSE_LESS, rpm.RPMDEP_SENSE_GREATER, and
|
|
* rpm.RPMDEP_SENSE_EQUAL can be logical ANDed with the needsFlags
|
|
* to get versioned dependency information.
|
|
* suggestedPackage is a tuple if the dependency check was aware
|
|
* of a package that solves this dependency problem when the
|
|
* dependency check was run. Packages that are added to the
|
|
* transaction set as "available" are examined during the
|
|
* dependency check as possible dependency solvers. The tuple
|
|
* contains two values, (header, suggestedName). These are set to
|
|
* the header of the suggested package and its name, respectively.
|
|
* If there is no known package to solve the dependency problem,
|
|
* suggestedPackage is None.
|
|
* The constants rpm.RPMDEP_SENSE_CONFLICTS and
|
|
* rpm.RPMDEP_SENSE_REQUIRES are set to show a dependency as a
|
|
* requirement or a conflict.
|
|
*
|
|
* - run(flags,problemSetFilter,callback,data) Attempt to execute a
|
|
* transaction set. After the transaction set has been populated
|
|
* with install and upgrade actions, it can be executed by invoking
|
|
* the run() method.
|
|
* @param flags - modifies the behavior of the transaction set as it is
|
|
* processed. The following values can be locical ORed
|
|
* together:
|
|
* - rpm.RPMTRANS_FLAG_TEST - test mode, do not modify the RPM
|
|
* database, change any files, or run any package scripts
|
|
* - rpm.RPMTRANS_FLAG_BUILD_PROBS - only build a list of
|
|
* problems encountered when attempting to run this transaction
|
|
* set
|
|
* - rpm.RPMTRANS_FLAG_NOSCRIPTS - do not execute package scripts
|
|
* - rpm.RPMTRANS_FLAG_JUSTDB - only make changes to the rpm
|
|
* database, do not modify files.
|
|
* - rpm.RPMTRANS_FLAG_NOTRIGGERS - do not run trigger scripts
|
|
* - rpm.RPMTRANS_FLAG_NODOCS - do not install files marked as %doc
|
|
* - rpm.RPMTRANS_FLAG_ALLFILES - create all files, even if a
|
|
* file is marked %config(missingok) and an upgrade is
|
|
* being performed.
|
|
* - rpm.RPMTRANS_FLAG_KEEPOBSOLETE - do not remove obsoleted
|
|
* packages.
|
|
* @param problemSetFilter - control bit(s) to ignore classes of problems,
|
|
* any of
|
|
* - rpm.RPMPROB_FILTER_IGNOREOS -
|
|
* - rpm.RPMPROB_FILTER_IGNOREARCH -
|
|
* - rpm.RPMPROB_FILTER_REPLACEPKG -
|
|
* - rpm.RPMPROB_FILTER_FORCERELOCATE -
|
|
* - rpm.RPMPROB_FILTER_REPLACENEWFILES -
|
|
* - rpm.RPMPROB_FILTER_REPLACEOLDFILES -
|
|
* - rpm.RPMPROB_FILTER_OLDPACKAGE -
|
|
* - rpm.RPMPROB_FILTER_DISKSPACE -
|
|
*/
|
|
|
|
/** \ingroup python
|
|
* \name Class: rpmtrans
|
|
*/
|
|
/*@{*/
|
|
|
|
/** \ingroup python
|
|
*/
|
|
struct rpmtransObject_s {
|
|
PyObject_HEAD;
|
|
rpmdbObject * dbo;
|
|
rpmTransactionSet ts;
|
|
PyObject * keyList; /* keeps reference counts correct */
|
|
FD_t scriptFd;
|
|
} ;
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * rpmtransAdd(rpmtransObject * s, PyObject * args) {
|
|
hdrObject * h;
|
|
PyObject * key;
|
|
char * how = NULL;
|
|
int isUpgrade = 0;
|
|
PyObject * hObj;
|
|
|
|
if (!PyArg_ParseTuple(args, "OO|s", &h, &key, &how)) return NULL;
|
|
|
|
hObj = (PyObject *) h;
|
|
if (hObj->ob_type != &hdrType) {
|
|
PyErr_SetString(PyExc_TypeError, "bad type for header argument");
|
|
return NULL;
|
|
}
|
|
|
|
if (how && strcmp(how, "a") && strcmp(how, "u") && strcmp(how, "i")) {
|
|
PyErr_SetString(PyExc_TypeError, "how argument must be \"u\", \"a\", or \"i\"");
|
|
return NULL;
|
|
} else if (how && !strcmp(how, "u"))
|
|
isUpgrade = 1;
|
|
|
|
if (how && !strcmp(how, "a"))
|
|
rpmtransAvailablePackage(s->ts, hdrGetHeader(h), key);
|
|
else
|
|
rpmtransAddPackage(s->ts, hdrGetHeader(h), NULL, key, isUpgrade, NULL);
|
|
|
|
/* This should increment the usage count for me */
|
|
if (key) {
|
|
PyList_Append(s->keyList, key);
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * rpmtransRemove(rpmtransObject * s, PyObject * args) {
|
|
char * name;
|
|
int count;
|
|
rpmdbMatchIterator mi;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &name))
|
|
return NULL;
|
|
|
|
/* XXX: Copied hack from ../lib/rpminstall.c, rpmErase() */
|
|
mi = rpmdbInitIterator(dbFromDb(s->dbo), RPMDBI_LABEL, name, 0);
|
|
count = rpmdbGetIteratorCount(mi);
|
|
if (count <= 0) {
|
|
PyErr_SetString(pyrpmError, "package not installed");
|
|
return NULL;
|
|
} else { /* XXX: Note that we automatically choose to remove all matches */
|
|
Header h;
|
|
while ((h = rpmdbNextIterator(mi)) != NULL) {
|
|
unsigned int recOffset = rpmdbGetIteratorOffset(mi);
|
|
if (recOffset) {
|
|
rpmtransRemovePackage(s->ts, recOffset);
|
|
}
|
|
}
|
|
}
|
|
rpmdbFreeIterator(mi);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * rpmtransDepCheck(rpmtransObject * s, PyObject * args) {
|
|
rpmDependencyConflict conflicts;
|
|
int numConflicts;
|
|
PyObject * list, * cf, * suggestions;
|
|
int i, j;
|
|
int allSuggestions = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "|i", &allSuggestions)) return NULL;
|
|
|
|
rpmdepCheck(s->ts, &conflicts, &numConflicts);
|
|
if (numConflicts) {
|
|
list = PyList_New(0);
|
|
|
|
/* XXX TODO: rpmlib-4.0.3 can return multiple suggested packages. */
|
|
for (i = 0; i < numConflicts; i++) {
|
|
if (!conflicts[i].suggestedPackages)
|
|
suggestions = Py_None;
|
|
else if (!allSuggestions)
|
|
suggestions = conflicts[i].suggestedPackages[0];
|
|
else {
|
|
suggestions = PyList_New(0);
|
|
|
|
for (j = 0; conflicts[i].suggestedPackages[j]; j++)
|
|
PyList_Append(suggestions,
|
|
conflicts[i].suggestedPackages[j]);
|
|
}
|
|
|
|
cf = Py_BuildValue("((sss)(ss)iOi)", conflicts[i].byName,
|
|
conflicts[i].byVersion, conflicts[i].byRelease,
|
|
|
|
conflicts[i].needsName,
|
|
conflicts[i].needsVersion,
|
|
|
|
conflicts[i].needsFlags,
|
|
suggestions,
|
|
conflicts[i].sense);
|
|
PyList_Append(list, (PyObject *) cf);
|
|
Py_DECREF(cf);
|
|
}
|
|
|
|
conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
|
|
|
|
return list;
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * rpmtransOrder(rpmtransObject * s, PyObject * args) {
|
|
if (!PyArg_ParseTuple(args, "")) return NULL;
|
|
|
|
rpmdepOrder(s->ts);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * py_rpmtransGetKeys(rpmtransObject * s, PyObject * args) {
|
|
const void **data = NULL;
|
|
int num, i;
|
|
PyObject *tuple;
|
|
|
|
rpmtransGetKeys(s->ts, &data, &num);
|
|
if (data == NULL || num <= 0) {
|
|
data = _free(data);
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
tuple = PyTuple_New(num);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
PyObject *obj;
|
|
obj = (data[i] ? (PyObject *) data[i] : Py_None);
|
|
Py_INCREF(obj);
|
|
PyTuple_SetItem(tuple, i, obj);
|
|
}
|
|
|
|
data = _free(data);
|
|
|
|
return tuple;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
struct tsCallbackType {
|
|
PyObject * cb;
|
|
PyObject * data;
|
|
int pythonError;
|
|
};
|
|
|
|
/** \ingroup python
|
|
* @todo Remove, there's no headerLink refcount on the pointer.
|
|
*/
|
|
static Header transactionSetHeader = NULL;
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static void * tsCallback(const void * hd, const rpmCallbackType what,
|
|
const unsigned long amount, const unsigned long total,
|
|
const void * pkgKey, rpmCallbackData data) {
|
|
struct tsCallbackType * cbInfo = data;
|
|
PyObject * args, * result;
|
|
int fd;
|
|
static FD_t fdt;
|
|
const Header h = (Header) hd;
|
|
|
|
if (cbInfo->pythonError) return NULL;
|
|
if (cbInfo->cb == Py_None) return NULL;
|
|
|
|
if (!pkgKey) pkgKey = Py_None;
|
|
transactionSetHeader = h;
|
|
|
|
args = Py_BuildValue("(illOO)", what, amount, total, pkgKey, cbInfo->data);
|
|
result = PyEval_CallObject(cbInfo->cb, args);
|
|
Py_DECREF(args);
|
|
|
|
if (!result) {
|
|
cbInfo->pythonError = 1;
|
|
return NULL;
|
|
}
|
|
|
|
if (what == RPMCALLBACK_INST_OPEN_FILE) {
|
|
if (!PyArg_Parse(result, "i", &fd)) {
|
|
cbInfo->pythonError = 1;
|
|
return NULL;
|
|
}
|
|
fdt = fdDup(fd);
|
|
|
|
Py_DECREF(result);
|
|
return fdt;
|
|
}
|
|
|
|
if (what == RPMCALLBACK_INST_CLOSE_FILE) {
|
|
Fclose (fdt);
|
|
}
|
|
|
|
Py_DECREF(result);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * rpmtransRun(rpmtransObject * s, PyObject * args) {
|
|
int flags, ignoreSet;
|
|
int rc, i;
|
|
PyObject * list, * prob;
|
|
rpmProblemSet probs;
|
|
struct tsCallbackType cbInfo;
|
|
|
|
if (!PyArg_ParseTuple(args, "iiOO", &flags, &ignoreSet, &cbInfo.cb,
|
|
&cbInfo.data))
|
|
return NULL;
|
|
|
|
cbInfo.pythonError = 0;
|
|
|
|
rc = rpmRunTransactions(s->ts, tsCallback, &cbInfo, NULL, &probs, flags,
|
|
ignoreSet);
|
|
|
|
if (cbInfo.pythonError) {
|
|
if (rc > 0)
|
|
rpmProblemSetFree(probs);
|
|
return NULL;
|
|
}
|
|
|
|
if (rc < 0) {
|
|
list = PyList_New(0);
|
|
return list;
|
|
} else if (!rc) {
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
list = PyList_New(0);
|
|
for (i = 0; i < probs->numProblems; i++) {
|
|
rpmProblem myprob = probs->probs + i;
|
|
prob = Py_BuildValue("s(isN)", rpmProblemString(myprob),
|
|
myprob->type,
|
|
myprob->str1,
|
|
PyLong_FromLongLong(myprob->ulong1));
|
|
PyList_Append(list, prob);
|
|
Py_DECREF(prob);
|
|
}
|
|
|
|
rpmProblemSetFree(probs);
|
|
|
|
return list;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static struct PyMethodDef rpmtransMethods[] = {
|
|
{"add", (PyCFunction) rpmtransAdd, 1 },
|
|
{"remove", (PyCFunction) rpmtransRemove, 1 },
|
|
{"depcheck", (PyCFunction) rpmtransDepCheck, 1 },
|
|
{"order", (PyCFunction) rpmtransOrder, 1 },
|
|
{"getKeys", (PyCFunction) py_rpmtransGetKeys, 1 },
|
|
{"run", (PyCFunction) rpmtransRun, 1 },
|
|
{NULL, NULL} /* sentinel */
|
|
};
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyObject * rpmtransGetAttr(rpmtransObject * o, char * name) {
|
|
return Py_FindMethod(rpmtransMethods, (PyObject *) o, name);
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static void rpmtransDealloc(PyObject * o) {
|
|
rpmtransObject * trans = (void *) o;
|
|
|
|
rpmtransFree(trans->ts);
|
|
if (trans->dbo) {
|
|
Py_DECREF(trans->dbo);
|
|
}
|
|
if (trans->scriptFd) Fclose(trans->scriptFd);
|
|
/* this will free the keyList, and decrement the ref count of all
|
|
the items on the list as well :-) */
|
|
Py_DECREF(trans->keyList);
|
|
PyMem_DEL(o);
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static int rpmtransSetAttr(rpmtransObject * o, char * name,
|
|
PyObject * val) {
|
|
int i;
|
|
|
|
if (!strcmp(name, "scriptFd")) {
|
|
if (!PyArg_Parse(val, "i", &i)) return 0;
|
|
if (i < 0) {
|
|
PyErr_SetString(PyExc_TypeError, "bad file descriptor");
|
|
return -1;
|
|
} else {
|
|
o->scriptFd = fdDup(i);
|
|
rpmtransSetScriptFd(o->ts, o->scriptFd);
|
|
}
|
|
} else {
|
|
PyErr_SetString(PyExc_AttributeError, name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** \ingroup python
|
|
*/
|
|
static PyTypeObject rpmtransType = {
|
|
PyObject_HEAD_INIT(NULL)
|
|
0, /* ob_size */
|
|
"rpmtrans", /* tp_name */
|
|
sizeof(rpmtransObject), /* tp_size */
|
|
0, /* tp_itemsize */
|
|
(destructor) rpmtransDealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
(getattrfunc) rpmtransGetAttr, /* tp_getattr */
|
|
(setattrfunc) rpmtransSetAttr, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
0, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
};
|
|
|
|
/*@}*/
|
|
|
|
/** \ingroup python
|
|
* \name Module: rpm
|
|
*/
|
|
/*@{*/
|
|
|
|
/**
|
|
*/
|
|
static PyObject * rpmtransCreate(PyObject * self, PyObject * args) {
|
|
rpmtransObject * o;
|
|
rpmdbObject * db = NULL;
|
|
char * rootPath = "/";
|
|
|
|
if (!PyArg_ParseTuple(args, "|sO", &rootPath, &db)) return NULL;
|
|
if (db && ((PyObject *) db)->ob_type != &rpmdbType) {
|
|
PyErr_SetString(PyExc_TypeError, "bad type for database argument");
|
|
return NULL;
|
|
}
|
|
|
|
o = (void *) PyObject_NEW(rpmtransObject, &rpmtransType);
|
|
|
|
Py_XINCREF(db);
|
|
o->dbo = db;
|
|
o->scriptFd = NULL;
|
|
o->ts = rpmtransCreateSet(db ? dbFromDb(db) : NULL, rootPath);
|
|
o->keyList = PyList_New(0);
|
|
|
|
return (void *) o;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * doAddMacro(PyObject * self, PyObject * args) {
|
|
char * name, * val;
|
|
|
|
if (!PyArg_ParseTuple(args, "ss", &name, &val))
|
|
return NULL;
|
|
|
|
addMacro(NULL, name, NULL, val, RMIL_DEFAULT);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * doDelMacro(PyObject * self, PyObject * args) {
|
|
char * name;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &name))
|
|
return NULL;
|
|
|
|
delMacro(NULL, name);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * archScore(PyObject * self, PyObject * args) {
|
|
char * arch;
|
|
int score;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &arch))
|
|
return NULL;
|
|
|
|
score = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch);
|
|
|
|
return Py_BuildValue("i", score);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static int psGetArchScore(Header h) {
|
|
void * pkgArch;
|
|
int type, count;
|
|
|
|
if (!headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count) ||
|
|
type == RPM_INT8_TYPE)
|
|
return 150;
|
|
else
|
|
return rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static int pkgCompareVer(void * first, void * second) {
|
|
struct packageInfo ** a = first;
|
|
struct packageInfo ** b = second;
|
|
int ret, score1, score2;
|
|
|
|
/* put packages w/o names at the end */
|
|
if (!(*a)->name) return 1;
|
|
if (!(*b)->name) return -1;
|
|
|
|
ret = xstrcasecmp((*a)->name, (*b)->name);
|
|
if (ret) return ret;
|
|
score1 = psGetArchScore((*a)->h);
|
|
if (!score1) return 1;
|
|
score2 = psGetArchScore((*b)->h);
|
|
if (!score2) return -1;
|
|
if (score1 < score2) return -1;
|
|
if (score1 > score2) return 1;
|
|
return rpmVersionCompare((*b)->h, (*a)->h);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static void pkgSort(struct pkgSet * psp) {
|
|
int i;
|
|
char *name;
|
|
|
|
if (psp->numPackages <= 0)
|
|
return;
|
|
|
|
qsort(psp->packages, psp->numPackages, sizeof(*psp->packages),
|
|
(void *) pkgCompareVer);
|
|
|
|
name = psp->packages[0]->name;
|
|
if (!name) {
|
|
psp->numPackages = 0;
|
|
return;
|
|
}
|
|
for (i = 1; i < psp->numPackages; i++) {
|
|
if (!psp->packages[i]->name) break;
|
|
if (!strcmp(psp->packages[i]->name, name))
|
|
psp->packages[i]->name = NULL;
|
|
else
|
|
name = psp->packages[i]->name;
|
|
}
|
|
|
|
qsort(psp->packages, psp->numPackages, sizeof(*psp->packages),
|
|
(void *) pkgCompareVer);
|
|
|
|
for (i = 0; i < psp->numPackages; i++)
|
|
if (!psp->packages[i]->name) break;
|
|
psp->numPackages = i;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * findUpgradeSet(PyObject * self, PyObject * args) {
|
|
PyObject * hdrList, * result;
|
|
char * root = "/";
|
|
int i;
|
|
struct pkgSet list;
|
|
hdrObject * hdr;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|s", &hdrList, &root)) return NULL;
|
|
|
|
if (!PyList_Check(hdrList)) {
|
|
PyErr_SetString(PyExc_TypeError, "list of headers expected");
|
|
return NULL;
|
|
}
|
|
|
|
list.numPackages = PyList_Size(hdrList);
|
|
list.packages = alloca(sizeof(list.packages) * list.numPackages);
|
|
for (i = 0; i < list.numPackages; i++) {
|
|
hdr = (hdrObject *) PyList_GetItem(hdrList, i);
|
|
if (((PyObject *) hdr)->ob_type != &hdrType) {
|
|
PyErr_SetString(PyExc_TypeError, "list of headers expected");
|
|
return NULL;
|
|
}
|
|
list.packages[i] = alloca(sizeof(struct packageInfo));
|
|
list.packages[i]->h = hdrGetHeader(hdr);
|
|
list.packages[i]->selected = 0;
|
|
list.packages[i]->data = hdr;
|
|
|
|
headerGetEntry(list.packages[i]->h, RPMTAG_NAME, NULL,
|
|
(void **) &list.packages[i]->name, NULL);
|
|
}
|
|
|
|
pkgSort (&list);
|
|
|
|
if (ugFindUpgradePackages(&list, root)) {
|
|
PyErr_SetString(pyrpmError, "error during upgrade check");
|
|
return NULL;
|
|
}
|
|
|
|
result = PyList_New(0);
|
|
for (i = 0; i < list.numPackages; i++) {
|
|
if (list.packages[i]->selected) {
|
|
PyList_Append(result, list.packages[i]->data);
|
|
/* Py_DECREF(list.packages[i]->data); */
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * rpmInitDB(PyObject * self, PyObject * args) {
|
|
char *root;
|
|
int forWrite = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "i|s", &forWrite, &root)) return NULL;
|
|
|
|
if (rpmdbInit(root, forWrite ? O_RDWR | O_CREAT: O_RDONLY)) {
|
|
char * errmsg = "cannot initialize database in %s";
|
|
char * errstr = NULL;
|
|
int errsize;
|
|
|
|
errsize = strlen(errmsg) + strlen(root);
|
|
errstr = alloca(errsize);
|
|
snprintf(errstr, errsize, errmsg, root);
|
|
PyErr_SetString(pyrpmError, errstr);
|
|
return NULL;
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return(Py_None);
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
static PyObject * errorCB = NULL, * errorData = NULL;
|
|
|
|
/**
|
|
*/
|
|
static void errorcb (void)
|
|
{
|
|
PyObject * result, * args = NULL;
|
|
|
|
if (errorData)
|
|
args = Py_BuildValue("(O)", errorData);
|
|
|
|
result = PyEval_CallObject(errorCB, args);
|
|
Py_XDECREF(args);
|
|
|
|
if (result == NULL) {
|
|
PyErr_Print();
|
|
PyErr_Clear();
|
|
}
|
|
Py_DECREF (result);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * errorSetCallback (PyObject * self, PyObject * args) {
|
|
PyObject *newCB = NULL, *newData = NULL;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|O", &newCB, &newData)) return NULL;
|
|
|
|
/* if we're getting a void*, set the error callback to this. */
|
|
/* also, we can possibly decref any python callbacks we had */
|
|
/* and set them to NULL. */
|
|
if (PyCObject_Check (newCB)) {
|
|
rpmErrorSetCallback (PyCObject_AsVoidPtr(newCB));
|
|
|
|
Py_XDECREF (errorCB);
|
|
Py_XDECREF (errorData);
|
|
|
|
errorCB = NULL;
|
|
errorData = NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
if (!PyCallable_Check (newCB)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
|
|
return NULL;
|
|
}
|
|
|
|
Py_XDECREF(errorCB);
|
|
Py_XDECREF(errorData);
|
|
|
|
errorCB = newCB;
|
|
errorData = newData;
|
|
|
|
Py_INCREF (errorCB);
|
|
Py_XINCREF (errorData);
|
|
|
|
return PyCObject_FromVoidPtr(rpmErrorSetCallback (errorcb), NULL);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * errorString (PyObject * self, PyObject * args) {
|
|
return PyString_FromString(rpmErrorString ());
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static PyObject * checkSig (PyObject * self, PyObject * args) {
|
|
char * filename;
|
|
int flags;
|
|
int rc = 255;
|
|
|
|
if (PyArg_ParseTuple(args, "si", &filename, &flags)) {
|
|
const char *av[2];
|
|
av[0] = filename;
|
|
av[1] = NULL;
|
|
rc = rpmCheckSig(flags, av);
|
|
}
|
|
return Py_BuildValue("i", rc);
|
|
}
|
|
|
|
/* hack to get the current header that's in the transaction set */
|
|
/**
|
|
*/
|
|
static PyObject * getTsHeader (PyObject * self, PyObject * args) {
|
|
if (!PyArg_ParseTuple(args, ""))
|
|
return NULL;
|
|
|
|
if (transactionSetHeader) {
|
|
return (PyObject *) createHeaderObject(transactionSetHeader);;
|
|
}
|
|
Py_INCREF(Py_None);
|
|
return (PyObject *) Py_None;
|
|
}
|
|
|
|
|
|
static PyObject * setVerbosity (PyObject * self, PyObject * args) {
|
|
int level;
|
|
|
|
if (!PyArg_ParseTuple(args, "i", &level))
|
|
return NULL;
|
|
|
|
rpmSetVerbosity(level);
|
|
|
|
Py_INCREF(Py_None);
|
|
return (PyObject *) Py_None;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
typedef struct FDlist_t FDlist;
|
|
|
|
/**
|
|
*/
|
|
struct FDlist_t {
|
|
FILE *f;
|
|
FD_t fd;
|
|
char *note;
|
|
FDlist *next;
|
|
} ;
|
|
|
|
/**
|
|
*/
|
|
static FDlist *fdhead = NULL;
|
|
|
|
/**
|
|
*/
|
|
static FDlist *fdtail = NULL;
|
|
|
|
/**
|
|
*/
|
|
static int closeCallback(FILE * f) {
|
|
FDlist *node, *last;
|
|
|
|
printf ("close callback on %p\n", f);
|
|
|
|
node = fdhead;
|
|
last = NULL;
|
|
while (node) {
|
|
if (node->f == f)
|
|
break;
|
|
last = node;
|
|
node = node->next;
|
|
}
|
|
if (node) {
|
|
if (last)
|
|
last->next = node->next;
|
|
else
|
|
fdhead = node->next;
|
|
printf ("closing %s %p\n", node->note, node->fd);
|
|
free (node->note);
|
|
node->fd = fdLink(node->fd, "closeCallback");
|
|
Fclose (node->fd);
|
|
while (node->fd)
|
|
node->fd = fdFree(node->fd, "closeCallback");
|
|
free (node);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
/**
|
|
*/
|
|
static PyObject * doFopen(PyObject * self, PyObject * args) {
|
|
char * path, * mode;
|
|
FDlist *node;
|
|
|
|
if (!PyArg_ParseTuple(args, "ss", &path, &mode))
|
|
return NULL;
|
|
|
|
node = malloc (sizeof(FDlist));
|
|
|
|
node->fd = Fopen(path, mode);
|
|
node->fd = fdLink(node->fd, "doFopen");
|
|
node->note = strdup (path);
|
|
|
|
if (!node->fd) {
|
|
PyErr_SetFromErrno(pyrpmError);
|
|
free (node);
|
|
return NULL;
|
|
}
|
|
|
|
if (Ferror(node->fd)) {
|
|
const char *err = Fstrerror(node->fd);
|
|
free(node);
|
|
if (err) {
|
|
PyErr_SetString(pyrpmError, err);
|
|
return NULL;
|
|
}
|
|
}
|
|
node->f = fdGetFp(node->fd);
|
|
printf ("opening %s fd = %p f = %p\n", node->note, node->fd, node->f);
|
|
if (!node->f) {
|
|
PyErr_SetString(pyrpmError, "FD_t has no FILE*");
|
|
free(node);
|
|
return NULL;
|
|
}
|
|
|
|
node->next = NULL;
|
|
if (!fdhead) {
|
|
fdhead = fdtail = node;
|
|
} else if (fdtail) {
|
|
fdtail->next = node;
|
|
} else {
|
|
fdhead = node;
|
|
}
|
|
fdtail = node;
|
|
|
|
return PyFile_FromFile (node->f, path, mode, closeCallback);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
*/
|
|
static PyMethodDef rpmModuleMethods[] = {
|
|
{ "TransactionSet", (PyCFunction) rpmtransCreate, METH_VARARGS, NULL },
|
|
{ "addMacro", (PyCFunction) doAddMacro, METH_VARARGS, NULL },
|
|
{ "delMacro", (PyCFunction) doDelMacro, METH_VARARGS, NULL },
|
|
{ "archscore", (PyCFunction) archScore, METH_VARARGS, NULL },
|
|
{ "findUpgradeSet", (PyCFunction) findUpgradeSet, METH_VARARGS, NULL },
|
|
{ "headerFromPackage", (PyCFunction) rpmHeaderFromPackage, METH_VARARGS, NULL },
|
|
{ "headerLoad", (PyCFunction) hdrLoad, METH_VARARGS, NULL },
|
|
{ "rhnLoad", (PyCFunction) rhnLoad, METH_VARARGS, NULL },
|
|
{ "initdb", (PyCFunction) rpmInitDB, METH_VARARGS, NULL },
|
|
{ "opendb", (PyCFunction) rpmOpenDB, METH_VARARGS, NULL },
|
|
{ "rebuilddb", (PyCFunction) rebuildDB, METH_VARARGS, NULL },
|
|
{ "mergeHeaderListFromFD", (PyCFunction) rpmMergeHeadersFromFD, METH_VARARGS, NULL },
|
|
{ "readHeaderListFromFD", (PyCFunction) rpmHeaderFromFD, METH_VARARGS, NULL },
|
|
{ "readHeaderListFromFile", (PyCFunction) rpmHeaderFromFile, METH_VARARGS, NULL },
|
|
{ "errorSetCallback", (PyCFunction) errorSetCallback, METH_VARARGS, NULL },
|
|
{ "errorString", (PyCFunction) errorString, METH_VARARGS, NULL },
|
|
{ "versionCompare", (PyCFunction) versionCompare, METH_VARARGS, NULL },
|
|
{ "labelCompare", (PyCFunction) labelCompare, METH_VARARGS, NULL },
|
|
{ "checksig", (PyCFunction) checkSig, METH_VARARGS, NULL },
|
|
{ "getTransactionCallbackHeader", (PyCFunction) getTsHeader, METH_VARARGS, NULL },
|
|
/* { "Fopen", (PyCFunction) doFopen, METH_VARARGS, NULL }, */
|
|
{ "setVerbosity", (PyCFunction) setVerbosity, METH_VARARGS, NULL },
|
|
{ NULL }
|
|
} ;
|
|
|
|
/**
|
|
*/
|
|
void initrpm(void) {
|
|
PyObject * m, * d, *o, * tag = NULL, * dict;
|
|
int i;
|
|
const struct headerSprintfExtension_s * extensions = rpmHeaderFormats;
|
|
struct headerSprintfExtension_s * ext;
|
|
|
|
m = Py_InitModule("rpm", rpmModuleMethods);
|
|
|
|
hdrType.ob_type = &PyType_Type;
|
|
rpmdbMIType.ob_type = &PyType_Type;
|
|
rpmdbType.ob_type = &PyType_Type;
|
|
rpmtransType.ob_type = &PyType_Type;
|
|
|
|
if(!m)
|
|
return;
|
|
|
|
/* _rpmio_debug = -1; */
|
|
rpmReadConfigFiles(NULL, NULL);
|
|
|
|
d = PyModule_GetDict(m);
|
|
|
|
pyrpmError = PyString_FromString("rpm.error");
|
|
PyDict_SetItemString(d, "error", pyrpmError);
|
|
Py_DECREF(pyrpmError);
|
|
|
|
dict = PyDict_New();
|
|
|
|
for (i = 0; i < rpmTagTableSize; i++) {
|
|
tag = PyInt_FromLong(rpmTagTable[i].val);
|
|
PyDict_SetItemString(d, (char *) rpmTagTable[i].name, tag);
|
|
Py_DECREF(tag);
|
|
PyDict_SetItem(dict, tag, o=PyString_FromString(rpmTagTable[i].name + 7));
|
|
Py_DECREF(o);
|
|
}
|
|
|
|
while (extensions->name) {
|
|
if (extensions->type == HEADER_EXT_TAG) {
|
|
ext = extensions;
|
|
PyDict_SetItemString(d, extensions->name, o=PyCObject_FromVoidPtr(ext, NULL));
|
|
Py_DECREF(o);
|
|
PyDict_SetItem(dict, tag, o=PyString_FromString(ext->name + 7));
|
|
Py_DECREF(o);
|
|
}
|
|
extensions++;
|
|
}
|
|
|
|
PyDict_SetItemString(d, "tagnames", dict);
|
|
Py_DECREF(dict);
|
|
|
|
|
|
#define REGISTER_ENUM(val) \
|
|
PyDict_SetItemString(d, #val, o=PyInt_FromLong( val )); \
|
|
Py_DECREF(o);
|
|
|
|
REGISTER_ENUM(RPMFILE_STATE_NORMAL);
|
|
REGISTER_ENUM(RPMFILE_STATE_REPLACED);
|
|
REGISTER_ENUM(RPMFILE_STATE_NOTINSTALLED);
|
|
REGISTER_ENUM(RPMFILE_STATE_NETSHARED);
|
|
|
|
REGISTER_ENUM(RPMFILE_CONFIG);
|
|
REGISTER_ENUM(RPMFILE_DOC);
|
|
REGISTER_ENUM(RPMFILE_MISSINGOK);
|
|
REGISTER_ENUM(RPMFILE_NOREPLACE);
|
|
REGISTER_ENUM(RPMFILE_GHOST);
|
|
REGISTER_ENUM(RPMFILE_LICENSE);
|
|
REGISTER_ENUM(RPMFILE_README);
|
|
|
|
REGISTER_ENUM(RPMDEP_SENSE_REQUIRES);
|
|
REGISTER_ENUM(RPMDEP_SENSE_CONFLICTS);
|
|
|
|
REGISTER_ENUM(RPMSENSE_SERIAL);
|
|
REGISTER_ENUM(RPMSENSE_LESS);
|
|
REGISTER_ENUM(RPMSENSE_GREATER);
|
|
REGISTER_ENUM(RPMSENSE_EQUAL);
|
|
REGISTER_ENUM(RPMSENSE_PREREQ);
|
|
REGISTER_ENUM(RPMSENSE_INTERP);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_PRE);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_POST);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_PREUN);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_POSTUN);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_VERIFY);
|
|
REGISTER_ENUM(RPMSENSE_FIND_REQUIRES);
|
|
REGISTER_ENUM(RPMSENSE_FIND_PROVIDES);
|
|
REGISTER_ENUM(RPMSENSE_TRIGGERIN);
|
|
REGISTER_ENUM(RPMSENSE_TRIGGERUN);
|
|
REGISTER_ENUM(RPMSENSE_TRIGGERPOSTUN);
|
|
REGISTER_ENUM(RPMSENSE_MULTILIB);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_PREP);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_BUILD);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_INSTALL);
|
|
REGISTER_ENUM(RPMSENSE_SCRIPT_CLEAN);
|
|
REGISTER_ENUM(RPMSENSE_RPMLIB);
|
|
REGISTER_ENUM(RPMSENSE_TRIGGERPREIN);
|
|
|
|
REGISTER_ENUM(RPMTRANS_FLAG_TEST);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_BUILD_PROBS);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_NOSCRIPTS);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_JUSTDB);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_NOTRIGGERS);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_NODOCS);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_ALLFILES);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_KEEPOBSOLETE);
|
|
REGISTER_ENUM(RPMTRANS_FLAG_MULTILIB);
|
|
|
|
REGISTER_ENUM(RPMPROB_FILTER_IGNOREOS);
|
|
REGISTER_ENUM(RPMPROB_FILTER_IGNOREARCH);
|
|
REGISTER_ENUM(RPMPROB_FILTER_REPLACEPKG);
|
|
REGISTER_ENUM(RPMPROB_FILTER_FORCERELOCATE);
|
|
REGISTER_ENUM(RPMPROB_FILTER_REPLACENEWFILES);
|
|
REGISTER_ENUM(RPMPROB_FILTER_REPLACEOLDFILES);
|
|
REGISTER_ENUM(RPMPROB_FILTER_OLDPACKAGE);
|
|
REGISTER_ENUM(RPMPROB_FILTER_DISKSPACE);
|
|
REGISTER_ENUM(RPMPROB_FILTER_DISKNODES);
|
|
|
|
REGISTER_ENUM(RPMCALLBACK_INST_PROGRESS);
|
|
REGISTER_ENUM(RPMCALLBACK_INST_START);
|
|
REGISTER_ENUM(RPMCALLBACK_INST_OPEN_FILE);
|
|
REGISTER_ENUM(RPMCALLBACK_INST_CLOSE_FILE);
|
|
REGISTER_ENUM(RPMCALLBACK_TRANS_PROGRESS);
|
|
REGISTER_ENUM(RPMCALLBACK_TRANS_START);
|
|
REGISTER_ENUM(RPMCALLBACK_TRANS_STOP);
|
|
REGISTER_ENUM(RPMCALLBACK_UNINST_PROGRESS);
|
|
REGISTER_ENUM(RPMCALLBACK_UNINST_START);
|
|
REGISTER_ENUM(RPMCALLBACK_UNINST_STOP);
|
|
REGISTER_ENUM(RPMCALLBACK_UNPACK_ERROR);
|
|
REGISTER_ENUM(RPMCALLBACK_CPIO_ERROR);
|
|
|
|
REGISTER_ENUM(RPMPROB_BADARCH);
|
|
REGISTER_ENUM(RPMPROB_BADOS);
|
|
REGISTER_ENUM(RPMPROB_PKG_INSTALLED);
|
|
REGISTER_ENUM(RPMPROB_BADRELOCATE);
|
|
REGISTER_ENUM(RPMPROB_REQUIRES);
|
|
REGISTER_ENUM(RPMPROB_CONFLICT);
|
|
REGISTER_ENUM(RPMPROB_NEW_FILE_CONFLICT);
|
|
REGISTER_ENUM(RPMPROB_FILE_CONFLICT);
|
|
REGISTER_ENUM(RPMPROB_OLDPACKAGE);
|
|
REGISTER_ENUM(RPMPROB_DISKSPACE);
|
|
REGISTER_ENUM(RPMPROB_DISKNODES);
|
|
REGISTER_ENUM(RPMPROB_BADPRETRANS);
|
|
|
|
REGISTER_ENUM(CHECKSIG_PGP);
|
|
REGISTER_ENUM(CHECKSIG_GPG);
|
|
REGISTER_ENUM(CHECKSIG_MD5);
|
|
|
|
REGISTER_ENUM(RPMLOG_EMERG);
|
|
REGISTER_ENUM(RPMLOG_ALERT);
|
|
REGISTER_ENUM(RPMLOG_CRIT);
|
|
REGISTER_ENUM(RPMLOG_ERR);
|
|
REGISTER_ENUM(RPMLOG_WARNING);
|
|
REGISTER_ENUM(RPMLOG_NOTICE);
|
|
REGISTER_ENUM(RPMLOG_INFO);
|
|
REGISTER_ENUM(RPMLOG_DEBUG);
|
|
|
|
REGISTER_ENUM(RPMMIRE_DEFAULT);
|
|
REGISTER_ENUM(RPMMIRE_STRCMP);
|
|
REGISTER_ENUM(RPMMIRE_REGEX);
|
|
REGISTER_ENUM(RPMMIRE_GLOB);
|
|
|
|
}
|
|
|
|
/*@}*/
|