2 * \file python/rpmts-py.c
7 #include <rpm/rpmlib.h> /* rpmReadPackageFile, headerCheck */
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmpgp.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmbuild.h>
14 #include "header-py.h"
15 #include "rpmds-py.h" /* XXX for rpmdsNew */
16 #include "rpmfi-py.h" /* XXX for rpmfiNew */
26 extern int _rpmts_debug;
32 * \brief A python rpm.ts object represents an RPM transaction set.
34 * The transaction set is the workhorse of RPM. It performs the
35 * installation and upgrade of packages. The rpm.ts object is
36 * instantiated by the TransactionSet function in the rpm module.
38 * The TransactionSet function takes two optional arguments. The first
39 * argument is the root path. The second is the verify signature disable flags,
40 * a set of the following bits:
42 * - rpm.RPMVSF_NOHDRCHK if set, don't check rpmdb headers
43 * - rpm.RPMVSF_NEEDPAYLOAD if not set, check header+payload (if possible)
44 * - rpm.RPMVSF_NOSHA1HEADER if set, don't check header SHA1 digest
45 * - rpm.RPMVSF_NODSAHEADER if set, don't check header DSA signature
46 * - rpm.RPMVSF_NOMD5 if set, don't check header+payload MD5 digest
47 * - rpm.RPMVSF_NODSA if set, don't check header+payload DSA signature
48 * - rpm.RPMVSF_NORSA if set, don't check header+payload RSA signature
50 * For convenience, there are the following masks:
51 * - rpm._RPMVSF_NODIGESTS if set, don't check digest(s).
52 * - rpm._RPMVSF_NOSIGNATURES if set, don't check signature(s).
54 * A rpm.ts object has the following methods:
56 * - addInstall(hdr,data,mode) Add an install element to a transaction set.
57 * @param hdr the header to be added
58 * @param data user data that will be passed to the transaction callback
59 * during transaction execution
60 * @param mode optional argument that specifies if this package should
61 * be installed ('i'), upgraded ('u'), or if it is just
62 * available to the transaction when computing
63 * dependencies but no action should be performed with it
66 * - addErase(name) Add an erase element to a transaction set.
67 * @param name the package name to be erased
69 * - check() Perform a dependency check on the transaction set. After
70 * headers have been added to a transaction set, a dependency
71 * check can be performed to make sure that all package
72 * dependencies are satisfied.
73 * @return None If there are no unresolved dependencies
74 * Otherwise a list of complex tuples is returned, one tuple per
75 * unresolved dependency, with
76 * The format of the dependency tuple is:
77 * ((packageName, packageVersion, packageRelease),
78 * (reqName, reqVersion),
82 * packageName, packageVersion, packageRelease are the name,
83 * version, and release of the package that has the unresolved
84 * dependency or conflict.
85 * The reqName and reqVersion are the name and version of the
86 * requirement or conflict.
87 * The needsFlags is a bitfield that describes the versioned
88 * nature of a requirement or conflict. The constants
89 * rpm.RPMSENSE_LESS, rpm.RPMSENSE_GREATER, and
90 * rpm.RPMSENSE_EQUAL can be logical ANDed with the needsFlags
91 * to get versioned dependency information.
92 * suggestedPackage is a tuple if the dependency check was aware
93 * of a package that solves this dependency problem when the
94 * dependency check was run. Packages that are added to the
95 * transaction set as "available" are examined during the
96 * dependency check as possible dependency solvers. The tuple
97 * contains two values, (header, suggestedName). These are set to
98 * the header of the suggested package and its name, respectively.
99 * If there is no known package to solve the dependency problem,
100 * suggestedPackage is None.
101 * The constants rpm.RPMDEP_SENSE_CONFLICTS and
102 * rpm.RPMDEP_SENSE_REQUIRES are set to show a dependency as a
103 * requirement or a conflict.
105 * - ts.order() Do a topological sort of added element relations.
108 * - ts.setFlags(transFlags) Set transaction set flags.
109 * @param transFlags - bit(s) to controll transaction operations. The
110 * following values can be logically OR'ed together:
111 * - rpm.RPMTRANS_FLAG_TEST - test mode, do not modify the RPM
112 * database, change any files, or run any package scripts
113 * - rpm.RPMTRANS_FLAG_BUILD_PROBS - only build a list of
114 * problems encountered when attempting to run this transaction
116 * - rpm.RPMTRANS_FLAG_NOSCRIPTS - do not execute package scripts
117 * - rpm.RPMTRANS_FLAG_JUSTDB - only make changes to the rpm
118 * database, do not modify files.
119 * - rpm.RPMTRANS_FLAG_NOTRIGGERS - do not run trigger scripts
120 * - rpm.RPMTRANS_FLAG_NODOCS - do not install files marked as %doc
121 * - rpm.RPMTRANS_FLAG_ALLFILES - create all files, even if a
122 * file is marked %config(missingok) and an upgrade is
124 * - rpm.RPMTRANS_FLAG_KEEPOBSOLETE - do not remove obsoleted
126 * @return previous transFlags
128 * - ts.setProbFilter(ignoreSet) Set transaction set problem filter.
129 * @param problemSetFilter - control bit(s) to ignore classes of problems,
130 * a logical or of one or more of the following bit(s):
131 * - rpm.RPMPROB_FILTER_IGNOREOS -
132 * - rpm.RPMPROB_FILTER_IGNOREARCH -
133 * - rpm.RPMPROB_FILTER_REPLACEPKG -
134 * - rpm.RPMPROB_FILTER_FORCERELOCATE -
135 * - rpm.RPMPROB_FILTER_REPLACENEWFILES -
136 * - rpm.RPMPROB_FILTER_REPLACEOLDFILES -
137 * - rpm.RPMPROB_FILTER_OLDPACKAGE -
138 * - rpm.RPMPROB_FILTER_DISKSPACE -
139 * @return previous ignoreSet
141 * - ts.run(callback,data) Attempt to execute a transaction set.
142 * After the transaction set has been populated with install/upgrade or
143 * erase actions, the transaction set can be executed by invoking
144 * the ts.run() method.
149 struct rpmtsCallbackType_s {
154 PyThreadState *_save;
160 rpmts_Debug(rpmtsObject * s, PyObject * args, PyObject * kwds)
162 char * kwlist[] = {"debugLevel", NULL};
164 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Debug", kwlist,
168 if (_rpmts_debug < 0)
169 fprintf(stderr, "*** rpmts_Debug(%p) ts %p\n", s, s->ts);
177 * Add package to universe of possible packages to install in transaction set.
178 * @param ts transaction set
180 * @param key package private data
182 static void rpmtsAddAvailableElement(rpmts ts, Header h,
186 rpmds provides = rpmdsNew(h, RPMTAG_PROVIDENAME, scareMem);
187 rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
189 /* XXX FIXME: return code RPMAL_NOMATCH is error */
190 (void) rpmalAdd(&ts->availablePackages, RPMAL_NOMATCH, key,
191 provides, fi, rpmtsColor(ts));
193 provides = rpmdsFree(provides);
195 if (_rpmts_debug < 0)
196 fprintf(stderr, "\tAddAvailable(%p) list %p\n", ts, ts->availablePackages);
204 rpmts_AddInstall(rpmtsObject * s, PyObject * args, PyObject * kwds)
208 char * how = "u"; /* XXX default to upgrade element if missing */
210 char * kwlist[] = {"header", "key", "how", NULL};
213 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|s:AddInstall", kwlist,
214 &hdr_Type, &h, &key, &how))
217 { PyObject * hObj = (PyObject *) h;
218 if (hObj->ob_type != &hdr_Type) {
219 PyErr_SetString(PyExc_TypeError, "bad type for header argument");
224 if (_rpmts_debug < 0 || (_rpmts_debug > 0 && *how != 'a'))
225 fprintf(stderr, "*** rpmts_AddInstall(%p,%p,%p,%s) ts %p\n", s, h, key, how, s->ts);
227 if (how && strcmp(how, "a") && strcmp(how, "u") && strcmp(how, "i")) {
228 PyErr_SetString(PyExc_TypeError, "how argument must be \"u\", \"a\", or \"i\"");
230 } else if (how && !strcmp(how, "u"))
234 * XXX resurrect when better available mechanism is, well, available.
235 * OTOH nothing appears to use it these days...
236 * Raise exception to catch out any callers while broken.
238 if (how && !strcmp(how, "a")) {
240 rpmtsAddAvailableElement(s->ts, hdrGetHeader(h), key);
242 PyErr_SetString(pyrpmError, "available package mechanism currently broken");
246 rc = rpmtsAddInstallElement(s->ts, hdrGetHeader(h), key, isUpgrade, NULL);
248 PyErr_SetString(pyrpmError, "adding package to transaction failed");
253 /* This should increment the usage count for me */
255 PyList_Append(s->keyList, key);
262 * @todo Permit finer control (i.e. not just --allmatches) of deleted elments.
265 rpmts_AddErase(rpmtsObject * s, PyObject * args, PyObject * kwds)
269 rpmdbMatchIterator mi;
270 char * kwlist[] = {"name", NULL};
273 fprintf(stderr, "*** rpmts_AddErase(%p) ts %p\n", s, s->ts);
275 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:AddErase", kwlist, &o))
278 if (PyString_Check(o)) {
279 char * name = PyString_AsString(o);
281 mi = rpmtsInitIterator(s->ts, RPMDBI_LABEL, name, 0);
282 count = rpmdbGetIteratorCount(mi);
284 mi = rpmdbFreeIterator(mi);
285 PyErr_SetString(pyrpmError, "package not installed");
287 } else { /* XXX: Note that we automatically choose to remove all matches */
289 while ((h = rpmdbNextIterator(mi)) != NULL) {
290 unsigned int recOffset = rpmdbGetIteratorOffset(mi);
292 rpmtsAddEraseElement(s->ts, h, recOffset);
295 mi = rpmdbFreeIterator(mi);
297 if (PyInt_Check(o)) {
298 uint32_t instance = PyInt_AsLong(o);
300 mi = rpmtsInitIterator(s->ts, RPMDBI_PACKAGES, &instance, sizeof(instance));
301 if (instance == 0 || mi == NULL) {
302 mi = rpmdbFreeIterator(mi);
303 PyErr_SetString(pyrpmError, "package not installed");
307 while ((h = rpmdbNextIterator(mi)) != NULL) {
308 uint32_t recOffset = rpmdbGetIteratorOffset(mi);
310 rpmtsAddEraseElement(s->ts, h, recOffset);
314 mi = rpmdbFreeIterator(mi);
324 rpmts_SolveCallback(rpmts ts, rpmds ds, const void * data)
326 struct rpmtsCallbackType_s * cbInfo = (struct rpmtsCallbackType_s *) data;
327 PyObject * args, * result;
331 fprintf(stderr, "*** rpmts_SolveCallback(%p,%p,%p) \"%s\"\n", ts, ds, data, rpmdsDNEVR(ds));
333 if (cbInfo->tso == NULL) return res;
334 if (cbInfo->pythonError) return res;
335 if (cbInfo->cb == Py_None) return res;
337 PyEval_RestoreThread(cbInfo->_save);
339 args = Py_BuildValue("(Oissi)", cbInfo->tso,
340 rpmdsTagN(ds), rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
341 result = PyEval_CallObject(cbInfo->cb, args);
345 cbInfo->pythonError = 1;
347 if (PyInt_Check(result))
348 res = PyInt_AsLong(result);
352 cbInfo->_save = PyEval_SaveThread();
360 rpmts_Check(rpmtsObject * s, PyObject * args, PyObject * kwds)
364 PyObject * list, * cf;
365 struct rpmtsCallbackType_s cbInfo;
368 char * kwlist[] = {"callback", NULL};
370 memset(&cbInfo, 0, sizeof(cbInfo));
371 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:Check", kwlist,
375 if (cbInfo.cb != NULL) {
376 if (!PyCallable_Check(cbInfo.cb)) {
377 PyErr_SetString(PyExc_TypeError, "expected a callable");
380 xx = rpmtsSetSolveCallback(s->ts, rpmts_SolveCallback, (void *)&cbInfo);
384 fprintf(stderr, "*** rpmts_Check(%p) ts %p cb %p\n", s, s->ts, cbInfo.cb);
387 cbInfo.pythonError = 0;
388 cbInfo._save = PyEval_SaveThread();
391 /* XXX resurrect availablePackages one more time ... */
392 rpmalMakeIndex(s->ts->availablePackages);
395 xx = rpmtsCheck(s->ts);
396 ps = rpmtsProblems(s->ts);
399 xx = rpmtsSetSolveCallback(s->ts, rpmtsSolve, NULL);
401 PyEval_RestoreThread(cbInfo._save);
404 list = PyList_New(0);
405 rpmpsi psi = rpmpsInitIterator(ps);
407 /* XXX TODO: rpmlib >= 4.0.3 can return multiple suggested keys. */
408 while ((i = rpmpsNextIterator(psi)) >= 0) {
409 const char * needsName;
410 char * byName, * byVersion, * byRelease, *byArch;
411 char * needsOP, * needsVersion;
412 rpmsenseFlags needsFlags, sense;
415 p = rpmpsGetProblem(psi);
417 /* XXX autorelocated i386 on ia64, fix system-config-packages! */
418 if (rpmProblemGetType(p) == RPMPROB_BADRELOCATE)
421 byName = strdup(rpmProblemGetPkgNEVR(p));
422 if ((byArch= strrchr(byName, '.')) != NULL)
424 if ((byRelease = strrchr(byName, '-')) != NULL)
426 if ((byVersion = strrchr(byName, '-')) != NULL)
429 key = rpmProblemGetKey(p);
431 needsName = rpmProblemGetAltNEVR(p);
432 if (needsName[1] == ' ') {
433 sense = (needsName[0] == 'C')
434 ? RPMDEP_SENSE_CONFLICTS : RPMDEP_SENSE_REQUIRES;
437 sense = RPMDEP_SENSE_REQUIRES;
438 if ((needsVersion = strrchr(needsName, ' ')) != NULL)
439 *needsVersion++ = '\0';
442 if ((needsOP = strrchr(needsName, ' ')) != NULL) {
443 for (*needsOP++ = '\0'; *needsOP != '\0'; needsOP++) {
444 if (*needsOP == '<') needsFlags |= RPMSENSE_LESS;
445 else if (*needsOP == '>') needsFlags |= RPMSENSE_GREATER;
446 else if (*needsOP == '=') needsFlags |= RPMSENSE_EQUAL;
450 cf = Py_BuildValue("((sss)(ss)iOi)", byName, byVersion, byRelease,
451 needsName, needsVersion, needsFlags,
452 (key != NULL ? key : Py_None),
454 PyList_Append(list, (PyObject *) cf);
459 psi = rpmpsFreeIterator(psi);
472 rpmts_Order(rpmtsObject * s)
477 fprintf(stderr, "*** rpmts_Order(%p) ts %p\n", s, s->ts);
479 Py_BEGIN_ALLOW_THREADS
480 rc = rpmtsOrder(s->ts);
483 return Py_BuildValue("i", rc);
489 rpmts_Clean(rpmtsObject * s)
492 fprintf(stderr, "*** rpmts_Clean(%p) ts %p\n", s, s->ts);
503 rpmts_IDTXload(rpmtsObject * s)
505 PyObject * result = NULL;
506 rpm_tag_t tag = RPMTAG_INSTALLTID;
510 fprintf(stderr, "*** rpmts_IDTXload(%p) ts %p\n", s, s->ts);
512 Py_BEGIN_ALLOW_THREADS
513 idtx = IDTXload(s->ts, tag);
516 if (idtx == NULL || idtx->nidt <= 0) {
525 result = PyTuple_New(idtx->nidt);
526 for (i = 0; i < idtx->nidt; i++) {
528 ho = (PyObject *) hdr_Wrap(idt->h);
529 tuple = Py_BuildValue("(iOi)", idt->val.u32, ho, idt->instance);
530 PyTuple_SET_ITEM(result, i, tuple);
535 idtx = IDTXfree(idtx);
543 rpmts_IDTXglob(rpmtsObject * s)
545 PyObject * result = NULL;
546 rpm_tag_t tag = RPMTAG_REMOVETID;
551 fprintf(stderr, "*** rpmts_IDTXglob(%p) ts %p\n", s, s->ts);
553 Py_BEGIN_ALLOW_THREADS
554 globstr = rpmExpand("%{_repackage_dir}/*.rpm", NULL);
555 idtx = IDTXglob(s->ts, globstr, tag);
556 globstr = _free(globstr);
559 if (idtx == NULL || idtx->nidt <= 0) {
568 result = PyTuple_New(idtx->nidt);
569 for (i = 0; i < idtx->nidt; i++) {
571 ho = (PyObject *) hdr_Wrap(idt->h);
572 tuple = Py_BuildValue("(iOs)", idt->val.u32, ho, idt->key);
573 PyTuple_SET_ITEM(result, i, tuple);
578 idtx = IDTXfree(idtx);
586 rpmts_Rollback(rpmtsObject * s, PyObject * args, PyObject * kwds)
588 struct rpmInstallArguments_s * ia = alloca(sizeof(*ia));
589 rpmtransFlags transFlags;
590 const char ** av = NULL;
593 char * kwlist[] = {"transactionId", NULL};
596 fprintf(stderr, "*** rpmts_Rollback(%p) ts %p\n", s, s->ts);
598 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Rollback", kwlist, &rbtid))
601 Py_BEGIN_ALLOW_THREADS
602 memset(ia, 0, sizeof(*ia));
603 ia->qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE|VERIFY_HDRCHK);
604 ia->transFlags |= (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL);
605 ia->transFlags |= RPMTRANS_FLAG_NOMD5;
606 ia->installInterfaceFlags = (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL);
608 ia->relocations = NULL;
609 ia->probFilter |= RPMPROB_FILTER_OLDPACKAGE;
611 transFlags = rpmtsSetFlags(s->ts, ia->transFlags);
612 rc = rpmRollback(s->ts, ia, av);
613 transFlags = rpmtsSetFlags(s->ts, transFlags);
616 return Py_BuildValue("i", rc);
622 rpmts_OpenDB(rpmtsObject * s)
627 fprintf(stderr, "*** rpmts_OpenDB(%p) ts %p\n", s, s->ts);
629 dbmode = rpmtsGetDBMode(s->ts);
633 return Py_BuildValue("i", rpmtsOpenDB(s->ts, dbmode));
639 rpmts_CloseDB(rpmtsObject * s)
644 fprintf(stderr, "*** rpmts_CloseDB(%p) ts %p\n", s, s->ts);
646 rc = rpmtsCloseDB(s->ts);
647 rpmtsSetDBMode(s->ts, -1); /* XXX disable lazy opens */
649 return Py_BuildValue("i", rc);
655 rpmts_InitDB(rpmtsObject * s)
660 fprintf(stderr, "*** rpmts_InitDB(%p) ts %p\n", s, s->ts);
662 rc = rpmtsInitDB(s->ts, O_RDONLY);
664 rc = rpmtsCloseDB(s->ts);
666 return Py_BuildValue("i", rc);
672 rpmts_RebuildDB(rpmtsObject * s)
677 fprintf(stderr, "*** rpmts_RebuildDB(%p) ts %p\n", s, s->ts);
679 Py_BEGIN_ALLOW_THREADS
680 rc = rpmtsRebuildDB(s->ts);
683 return Py_BuildValue("i", rc);
689 rpmts_VerifyDB(rpmtsObject * s)
694 fprintf(stderr, "*** rpmts_VerifyDB(%p) ts %p\n", s, s->ts);
696 Py_BEGIN_ALLOW_THREADS
697 rc = rpmtsVerifyDB(s->ts);
700 return Py_BuildValue("i", rc);
706 rpmts_HdrFromFdno(rpmtsObject * s, PyObject * args, PyObject * kwds)
708 PyObject * result = NULL;
713 char * kwlist[] = {"fd", NULL};
715 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:HdrFromFdno", kwlist,
720 rpmrc = rpmReadPackageFile(s->ts, fd, "rpmts_HdrFromFdno", &h);
724 fprintf(stderr, "*** rpmts_HdrFromFdno(%p) ts %p rc %d\n", s, s->ts, rpmrc);
729 result = Py_BuildValue("N", hdr_Wrap(h));
730 h = headerFree(h); /* XXX ref held by result */
734 PyErr_SetString(pyrpmError, "public key not available");
737 case RPMRC_NOTTRUSTED:
738 PyErr_SetString(pyrpmError, "public key not trusted");
744 PyErr_SetString(pyrpmError, "error reading package header");
754 rpmts_HdrCheck(rpmtsObject * s, PyObject * args, PyObject * kwds)
757 PyObject * result = NULL;
762 char * kwlist[] = {"headers", NULL};
765 fprintf(stderr, "*** rpmts_HdrCheck(%p) ts %p\n", s, s->ts);
767 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:HdrCheck", kwlist, &blob))
770 if (blob == Py_None) {
774 if (!PyString_Check(blob)) {
775 PyErr_SetString(pyrpmError, "hdrCheck takes a string of octets");
778 uh = PyString_AsString(blob);
779 uc = PyString_Size(blob);
781 rpmrc = headerCheck(s->ts, uh, uc, &msg);
790 PyErr_SetString(pyrpmError, "public key not availaiable");
793 case RPMRC_NOTTRUSTED:
794 PyErr_SetString(pyrpmError, "public key not trusted");
799 PyErr_SetString(pyrpmError, msg);
810 rpmts_SetVSFlags(rpmtsObject * s, PyObject * args, PyObject * kwds)
813 char * kwlist[] = {"flags", NULL};
816 fprintf(stderr, "*** rpmts_SetVSFlags(%p) ts %p\n", s, s->ts);
818 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetVSFlags", kwlist,
822 /* XXX FIXME: value check on vsflags, or build pure python object
823 * for it, and require an object of that type */
825 return Py_BuildValue("i", rpmtsSetVSFlags(s->ts, vsflags));
831 rpmts_GetVSFlags(rpmtsObject * s)
833 return Py_BuildValue("i", rpmtsVSFlags(s->ts));
839 rpmts_SetColor(rpmtsObject * s, PyObject * args, PyObject * kwds)
842 char * kwlist[] = {"color", NULL};
845 fprintf(stderr, "*** rpmts_SetColor(%p) ts %p\n", s, s->ts);
847 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Color", kwlist, &tscolor))
850 /* XXX FIXME: value check on tscolor, or build pure python object
851 * for it, and require an object of that type */
853 return Py_BuildValue("i", rpmtsSetColor(s->ts, tscolor));
859 rpmts_PgpPrtPkts(rpmtsObject * s, PyObject * args, PyObject * kwds)
865 char * kwlist[] = {"octets", NULL};
868 fprintf(stderr, "*** rpmts_PgpPrtPkts(%p) ts %p\n", s, s->ts);
870 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PgpPrtPkts", kwlist, &blob))
873 if (blob == Py_None) {
877 if (!PyString_Check(blob)) {
878 PyErr_SetString(pyrpmError, "pgpPrtPkts takes a string of octets");
881 pkt = (unsigned char *)PyString_AsString(blob);
882 pktlen = PyString_Size(blob);
884 rc = pgpPrtPkts(pkt, pktlen, NULL, 1);
886 return Py_BuildValue("i", rc);
892 rpmts_PgpImportPubkey(rpmtsObject * s, PyObject * args, PyObject * kwds)
898 char * kwlist[] = {"pubkey", NULL};
901 fprintf(stderr, "*** rpmts_PgpImportPubkey(%p) ts %p\n", s, s->ts);
903 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PgpImportPubkey",
907 if (blob == Py_None) {
911 if (!PyString_Check(blob)) {
912 PyErr_SetString(pyrpmError, "PgpImportPubkey takes a string of octets");
915 pkt = (unsigned char *)PyString_AsString(blob);
916 pktlen = PyString_Size(blob);
918 rc = rpmtsImportPubkey(s->ts, pkt, pktlen);
920 return Py_BuildValue("i", rc);
926 rpmts_GetKeys(rpmtsObject * s)
928 const void **data = NULL;
933 fprintf(stderr, "*** rpmts_GetKeys(%p) ts %p\n", s, s->ts);
935 rpmtsGetKeys(s->ts, &data, &num);
936 if (data == NULL || num <= 0) {
942 tuple = PyTuple_New(num);
944 for (i = 0; i < num; i++) {
946 obj = (data[i] ? (PyObject *) data[i] : Py_None);
948 PyTuple_SetItem(tuple, i, obj);
959 rpmtsCallback(const void * hd, const rpmCallbackType what,
960 const rpm_off_t amount, const rpm_off_t total,
961 const void * pkgKey, rpmCallbackData data)
963 Header h = (Header) hd;
964 struct rpmtsCallbackType_s * cbInfo = data;
965 PyObject * pkgObj = (PyObject *) pkgKey;
966 PyObject * args, * result;
969 if (cbInfo->pythonError) return NULL;
970 if (cbInfo->cb == Py_None) return NULL;
972 /* Synthesize a python object for callback (if necessary). */
973 if (pkgObj == NULL) {
975 const char * n = NULL;
976 (void) headerNVR(h, &n, NULL, NULL);
977 pkgObj = Py_BuildValue("s", n);
985 PyEval_RestoreThread(cbInfo->_save);
987 args = Py_BuildValue("(illOO)", what, amount, total, pkgObj, cbInfo->data);
988 result = PyEval_CallObject(cbInfo->cb, args);
993 cbInfo->pythonError = 1;
994 cbInfo->_save = PyEval_SaveThread();
998 if (what == RPMCALLBACK_INST_OPEN_FILE) {
1001 if (!PyArg_Parse(result, "i", &fdno)) {
1002 cbInfo->pythonError = 1;
1003 cbInfo->_save = PyEval_SaveThread();
1007 cbInfo->_save = PyEval_SaveThread();
1011 fprintf(stderr, "\t%p = fdDup(%d)\n", fd, fdno);
1013 fcntl(Fileno(fd), F_SETFD, FD_CLOEXEC);
1017 if (what == RPMCALLBACK_INST_CLOSE_FILE) {
1019 fprintf(stderr, "\tFclose(%p)\n", fd);
1023 fprintf(stderr, "\t%ld:%ld key %p\n", amount, total, pkgKey);
1027 cbInfo->_save = PyEval_SaveThread();
1035 rpmts_SetFlags(rpmtsObject * s, PyObject * args, PyObject * kwds)
1037 rpmtransFlags transFlags = 0;
1038 char * kwlist[] = {"flags", NULL};
1040 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetFlags", kwlist,
1045 fprintf(stderr, "*** rpmts_SetFlags(%p) ts %p transFlags %x\n", s, s->ts, transFlags);
1047 /* XXX FIXME: value check on flags, or build pure python object
1048 * for it, and require an object of that type */
1050 return Py_BuildValue("i", rpmtsSetFlags(s->ts, transFlags));
1056 rpmts_SetProbFilter(rpmtsObject * s, PyObject * args, PyObject * kwds)
1058 rpmprobFilterFlags ignoreSet = 0;
1059 rpmprobFilterFlags oignoreSet;
1060 char * kwlist[] = {"ignoreSet", NULL};
1062 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:ProbFilter", kwlist,
1067 fprintf(stderr, "*** rpmts_SetProbFilter(%p) ts %p ignoreSet %x\n", s, s->ts, ignoreSet);
1069 oignoreSet = s->ignoreSet;
1070 s->ignoreSet = ignoreSet;
1072 return Py_BuildValue("i", oignoreSet);
1077 static rpmpsObject *
1078 rpmts_Problems(rpmtsObject * s)
1082 fprintf(stderr, "*** rpmts_Problems(%p) ts %p\n", s, s->ts);
1084 return rpmps_Wrap( rpmtsProblems(s->ts) );
1090 rpmts_Run(rpmtsObject * s, PyObject * args, PyObject * kwds)
1096 struct rpmtsCallbackType_s cbInfo;
1097 char * kwlist[] = {"callback", "data", NULL};
1099 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:Run", kwlist,
1100 &cbInfo.cb, &cbInfo.data))
1104 cbInfo.pythonError = 0;
1105 cbInfo._save = PyEval_SaveThread();
1107 if (cbInfo.cb != NULL) {
1108 if (!PyCallable_Check(cbInfo.cb)) {
1109 PyErr_SetString(PyExc_TypeError, "expected a callable");
1112 (void) rpmtsSetNotifyCallback(s->ts, rpmtsCallback, (void *) &cbInfo);
1116 fprintf(stderr, "*** rpmts_Run(%p) ts %p ignore %x\n", s, s->ts, s->ignoreSet);
1118 rc = rpmtsRun(s->ts, NULL, s->ignoreSet);
1119 ps = rpmtsProblems(s->ts);
1122 (void) rpmtsSetNotifyCallback(s->ts, NULL, NULL);
1124 PyEval_RestoreThread(cbInfo._save);
1126 if (cbInfo.pythonError) {
1132 list = PyList_New(0);
1139 list = PyList_New(0);
1140 psi = rpmpsInitIterator(ps);
1141 while (rpmpsNextIterator(psi) >= 0) {
1142 rpmProblem p = rpmpsGetProblem(psi);
1143 char * ps = rpmProblemString(p);
1144 PyObject * prob = Py_BuildValue("s(isN)", ps,
1145 rpmProblemGetType(p),
1146 rpmProblemGetStr(p),
1147 PyLong_FromLongLong(rpmProblemGetLong(p)));
1148 PyList_Append(list, prob);
1153 psi = rpmpsFreeIterator(psi);
1159 #if Py_TPFLAGS_HAVE_ITER
1161 rpmts_iter(rpmtsObject * s)
1164 fprintf(stderr, "*** rpmts_iter(%p) ts %p\n", s, s->ts);
1167 return (PyObject *)s;
1172 * @todo Add TR_ADDED filter to iterator.
1175 rpmts_iternext(rpmtsObject * s)
1177 PyObject * result = NULL;
1181 fprintf(stderr, "*** rpmts_iternext(%p) ts %p tsi %p %d\n", s, s->ts, s->tsi, s->tsiFilter);
1183 /* Reset iterator on 1st entry. */
1184 if (s->tsi == NULL) {
1185 s->tsi = rpmtsiInit(s->ts);
1191 te = rpmtsiNext(s->tsi, s->tsiFilter);
1193 result = (PyObject *) rpmte_Wrap(te);
1195 s->tsi = rpmtsiFree(s->tsi);
1203 * @todo Add TR_ADDED filter to iterator.
1206 rpmts_Next(rpmtsObject * s)
1211 fprintf(stderr, "*** rpmts_Next(%p) ts %p\n", s, s->ts);
1213 result = rpmts_iternext(s);
1215 if (result == NULL) {
1226 spec_Parse(rpmtsObject * s, PyObject * args, PyObject * kwds)
1228 const char * specfile;
1230 char * buildRoot = NULL;
1232 char * passPhrase = "";
1233 char *cookie = NULL;
1236 char * kwlist[] = {"specfile", NULL};
1238 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:Parse", kwlist, &specfile))
1241 if (parseSpec(s->ts, specfile,"/", buildRoot,recursing, passPhrase,
1242 cookie, anyarch, force)!=0) {
1243 PyErr_SetString(pyrpmError, "can't parse specfile\n");
1247 spec = rpmtsSpec(s->ts);
1248 return spec_Wrap(spec);
1253 static rpmmiObject *
1254 rpmts_Match(rpmtsObject * s, PyObject * args, PyObject * kwds)
1256 PyObject *TagN = NULL;
1257 PyObject *Key = NULL;
1259 /* XXX lkey *must* be a 32 bit integer, int "works" on all known platforms. */
1262 rpm_tag_t tag = RPMDBI_PACKAGES;
1263 char * kwlist[] = {"tagNumber", "key", NULL};
1266 fprintf(stderr, "*** rpmts_Match(%p) ts %p\n", s, s->ts);
1268 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:Match", kwlist,
1272 if (TagN && (tag = tagNumFromPyObject (TagN)) == -1) {
1273 PyErr_SetString(PyExc_TypeError, "unknown tag type");
1278 if (PyString_Check(Key) || PyUnicode_Check(Key)) {
1279 key = PyString_AsString(Key);
1280 len = PyString_Size(Key);
1281 } else if (PyInt_Check(Key)) {
1282 lkey = PyInt_AsLong(Key);
1283 key = (char *)&lkey;
1286 PyErr_SetString(PyExc_TypeError, "unknown key type");
1291 /* XXX If not already opened, open the database O_RDONLY now. */
1292 /* XXX FIXME: lazy default rdonly open also done by rpmtsInitIterator(). */
1293 if (rpmtsGetRdb(s->ts) == NULL) {
1294 int rc = rpmtsOpenDB(s->ts, O_RDONLY);
1295 if (rc || rpmtsGetRdb(s->ts) == NULL) {
1296 PyErr_SetString(PyExc_TypeError, "rpmdb open failed");
1301 return rpmmi_Wrap( rpmtsInitIterator(s->ts, tag, key, len), (PyObject*)s);
1306 static struct PyMethodDef rpmts_methods[] = {
1307 {"Debug", (PyCFunction)rpmts_Debug, METH_VARARGS|METH_KEYWORDS,
1310 {"addInstall", (PyCFunction) rpmts_AddInstall, METH_VARARGS|METH_KEYWORDS,
1312 {"addErase", (PyCFunction) rpmts_AddErase, METH_VARARGS|METH_KEYWORDS,
1314 {"check", (PyCFunction) rpmts_Check, METH_VARARGS|METH_KEYWORDS,
1316 {"order", (PyCFunction) rpmts_Order, METH_NOARGS,
1318 {"setFlags", (PyCFunction) rpmts_SetFlags, METH_VARARGS|METH_KEYWORDS,
1319 "ts.setFlags(transFlags) -> previous transFlags\n\
1320 - Set control bit(s) for executing ts.run().\n\
1321 Note: This method replaces the 1st argument to the old ts.run()\n" },
1322 {"setProbFilter", (PyCFunction) rpmts_SetProbFilter, METH_VARARGS|METH_KEYWORDS,
1323 "ts.setProbFilter(ignoreSet) -> previous ignoreSet\n\
1324 - Set control bit(s) for ignoring problems found by ts.run().\n\
1325 Note: This method replaces the 2nd argument to the old ts.run()\n" },
1326 {"problems", (PyCFunction) rpmts_Problems, METH_NOARGS,
1327 "ts.problems() -> ps\n\
1328 - Return current problem set.\n" },
1329 {"run", (PyCFunction) rpmts_Run, METH_VARARGS|METH_KEYWORDS,
1330 "ts.run(callback, data) -> (problems)\n\
1331 - Run a transaction set, returning list of problems found.\n\
1332 Note: The callback may not be None.\n" },
1333 {"clean", (PyCFunction) rpmts_Clean, METH_NOARGS,
1335 {"IDTXload", (PyCFunction) rpmts_IDTXload, METH_NOARGS,
1336 "ts.IDTXload() -> ((tid,hdr,instance)+)\n\
1337 - Return list of installed packages reverse sorted by transaction id.\n" },
1338 {"IDTXglob", (PyCFunction) rpmts_IDTXglob, METH_NOARGS,
1339 "ts.IDTXglob() -> ((tid,hdr,instance)+)\n\
1340 - Return list of removed packages reverse sorted by transaction id.\n" },
1341 {"rollback", (PyCFunction) rpmts_Rollback, METH_VARARGS|METH_KEYWORDS,
1343 {"openDB", (PyCFunction) rpmts_OpenDB, METH_NOARGS,
1344 "ts.openDB() -> None\n\
1345 - Open the default transaction rpmdb.\n\
1346 Note: The transaction rpmdb is lazily opened, so ts.openDB() is seldom needed.\n" },
1347 {"closeDB", (PyCFunction) rpmts_CloseDB, METH_NOARGS,
1348 "ts.closeDB() -> None\n\
1349 - Close the default transaction rpmdb.\n\
1350 Note: ts.closeDB() disables lazy opens, and should hardly ever be used.\n" },
1351 {"initDB", (PyCFunction) rpmts_InitDB, METH_NOARGS,
1352 "ts.initDB() -> None\n\
1353 - Initialize the default transaction rpmdb.\n\
1354 Note: ts.initDB() is seldom needed anymore.\n" },
1355 {"rebuildDB", (PyCFunction) rpmts_RebuildDB, METH_NOARGS,
1356 "ts.rebuildDB() -> None\n\
1357 - Rebuild the default transaction rpmdb.\n" },
1358 {"verifyDB", (PyCFunction) rpmts_VerifyDB, METH_NOARGS,
1359 "ts.verifyDB() -> None\n\
1360 - Verify the default transaction rpmdb.\n" },
1361 {"hdrFromFdno",(PyCFunction) rpmts_HdrFromFdno,METH_VARARGS|METH_KEYWORDS,
1362 "ts.hdrFromFdno(fdno) -> hdr\n\
1363 - Read a package header from a file descriptor.\n" },
1364 {"hdrCheck", (PyCFunction) rpmts_HdrCheck, METH_VARARGS|METH_KEYWORDS,
1366 {"setVSFlags",(PyCFunction) rpmts_SetVSFlags, METH_VARARGS|METH_KEYWORDS,
1367 "ts.setVSFlags(vsflags) -> ovsflags\n\
1368 - Set signature verification flags. Values for vsflags are:\n\
1369 rpm.RPMVSF_NOHDRCHK if set, don't check rpmdb headers\n\
1370 rpm.RPMVSF_NEEDPAYLOAD if not set, check header+payload (if possible)\n\
1371 rpm.RPMVSF_NOSHA1HEADER if set, don't check header SHA1 digest\n\
1372 rpm.RPMVSF_NODSAHEADER if set, don't check header DSA signature\n\
1373 rpm.RPMVSF_NOMD5 if set, don't check header+payload MD5 digest\n\
1374 rpm.RPMVSF_NODSA if set, don't check header+payload DSA signature\n\
1375 rpm.RPMVSF_NORSA if set, don't check header+payload RSA signature\n\
1376 rpm._RPMVSF_NODIGESTS if set, don't check digest(s)\n\
1377 rpm._RPMVSF_NOSIGNATURES if set, don't check signature(s)\n" },
1378 {"getVSFlags",(PyCFunction) rpmts_GetVSFlags, METH_NOARGS,
1379 "ts.getVSFlags() -> vsflags\n\
1380 - Retrieve current signature verification flags from transaction\n" },
1381 {"setColor",(PyCFunction) rpmts_SetColor, METH_VARARGS|METH_KEYWORDS,
1383 {"pgpPrtPkts", (PyCFunction) rpmts_PgpPrtPkts, METH_VARARGS|METH_KEYWORDS,
1385 {"pgpImportPubkey", (PyCFunction) rpmts_PgpImportPubkey, METH_VARARGS|METH_KEYWORDS,
1387 {"getKeys", (PyCFunction) rpmts_GetKeys, METH_NOARGS,
1389 {"parseSpec", (PyCFunction) spec_Parse, METH_VARARGS|METH_KEYWORDS,
1390 "ts.parseSpec(\"/path/to/foo.spec\") -> spec\n\
1391 - Parse a spec file.\n" },
1392 {"dbMatch", (PyCFunction) rpmts_Match, METH_VARARGS|METH_KEYWORDS,
1393 "ts.dbMatch([TagN, [key, [len]]]) -> mi\n\
1394 - Create a match iterator for the default transaction rpmdb.\n" },
1395 {"next", (PyCFunction)rpmts_Next, METH_NOARGS,
1397 - Retrieve next transaction set element.\n" },
1398 {NULL, NULL} /* sentinel */
1403 static void rpmts_dealloc(rpmtsObject * s)
1407 fprintf(stderr, "%p -- ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1408 s->ts = rpmtsFree(s->ts);
1410 if (s->scriptFd) Fclose(s->scriptFd);
1411 /* this will free the keyList, and decrement the ref count of all
1412 the items on the list as well :-) */
1413 Py_DECREF(s->keyList);
1414 PyObject_Del((PyObject *)s);
1417 static PyObject * rpmts_getattro(PyObject * o, PyObject * n)
1419 return PyObject_GenericGetAttr(o, n);
1424 static int rpmts_setattro(PyObject * o, PyObject * n, PyObject * v)
1426 rpmtsObject *s = (rpmtsObject *)o;
1427 char * name = PyString_AsString(n);
1430 if (!strcmp(name, "scriptFd")) {
1431 if (!PyArg_Parse(v, "i", &fdno)) return 0;
1433 PyErr_SetString(PyExc_TypeError, "bad file descriptor");
1436 s->scriptFd = fdDup(fdno);
1437 rpmtsSetScriptFd(s->ts, s->scriptFd);
1440 PyErr_SetString(PyExc_AttributeError, name);
1449 static int rpmts_init(rpmtsObject * s, PyObject *args, PyObject *kwds)
1451 /* nothing to do atm... */
1457 static void rpmts_free(rpmtsObject * s)
1460 fprintf(stderr, "%p -- ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1461 s->ts = rpmtsFree(s->ts);
1464 Fclose(s->scriptFd);
1466 /* this will free the keyList, and decrement the ref count of all
1467 the items on the list as well :-) */
1468 Py_DECREF(s->keyList);
1470 PyObject_Del((PyObject *)s);
1475 static PyObject * rpmts_alloc(PyTypeObject * subtype, int nitems)
1477 PyObject * s = PyType_GenericAlloc(subtype, nitems);
1479 if (_rpmts_debug < 0)
1480 fprintf(stderr, "*** rpmts_alloc(%p,%d) ret %p\n", subtype, nitems, s);
1486 static PyObject * rpmts_new(PyTypeObject * subtype, PyObject *args, PyObject *kwds)
1488 rpmtsObject * s = (void *) PyObject_New(rpmtsObject, subtype);
1490 char * rootDir = "/";
1491 int vsflags = rpmExpandNumeric("%{?__vsflags}");
1492 char * kwlist[] = {"rootdir", "vsflags", 0};
1494 if (_rpmts_debug < 0)
1495 fprintf(stderr, "*** rpmts_new(%p,%p,%p)\n", s, args, kwds);
1497 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si:rpmts_init", kwlist,
1498 &rootDir, &vsflags))
1501 s->ts = rpmtsCreate();
1502 /* XXX: Why is there no rpmts_SetRootDir() ? */
1503 (void) rpmtsSetRootDir(s->ts, rootDir);
1504 /* XXX: make this use common code with rpmts_SetVSFlags() to check the
1506 (void) rpmtsSetVSFlags(s->ts, vsflags);
1507 s->keyList = PyList_New(0);
1513 fprintf(stderr, "%p ++ ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1515 return (PyObject *)s;
1520 static char rpmts_doc[] =
1525 PyTypeObject rpmts_Type = {
1526 PyObject_HEAD_INIT(&PyType_Type)
1528 "rpm.ts", /* tp_name */
1529 sizeof(rpmtsObject), /* tp_size */
1530 0, /* tp_itemsize */
1531 (destructor) rpmts_dealloc, /* tp_dealloc */
1533 (getattrfunc)0, /* tp_getattr */
1534 (setattrfunc)0, /* tp_setattr */
1537 0, /* tp_as_number */
1538 0, /* tp_as_sequence */
1539 0, /* tp_as_mapping */
1543 (getattrofunc) rpmts_getattro, /* tp_getattro */
1544 (setattrofunc) rpmts_setattro, /* tp_setattro */
1545 0, /* tp_as_buffer */
1546 Py_TPFLAGS_DEFAULT, /* tp_flags */
1547 rpmts_doc, /* tp_doc */
1548 #if Py_TPFLAGS_HAVE_ITER
1549 0, /* tp_traverse */
1551 0, /* tp_richcompare */
1552 0, /* tp_weaklistoffset */
1553 (getiterfunc) rpmts_iter, /* tp_iter */
1554 (iternextfunc) rpmts_iternext, /* tp_iternext */
1555 rpmts_methods, /* tp_methods */
1560 0, /* tp_descr_get */
1561 0, /* tp_descr_set */
1562 0, /* tp_dictoffset */
1563 (initproc) rpmts_init, /* tp_init */
1564 (allocfunc) rpmts_alloc, /* tp_alloc */
1565 (newfunc) rpmts_new, /* tp_new */
1566 (freefunc) rpmts_free, /* tp_free */
1574 rpmts_Create(PyObject * self, PyObject * args, PyObject * kwds)
1576 return PyObject_Call((PyObject *) &rpmts_Type, args, kwds);