3 * Routine(s) to handle a "rpmts" transaction sets.
10 #include <rpmmacro.h> /* XXX rpmtsOpenDB() needs rpmGetPath */
11 #include <rpmstring.h>
23 #include "rpmts_internal.h"
25 /* XXX FIXME: merge with existing (broken?) tests in system.h */
26 /* portability fiddles */
27 #if STATFS_IN_SYS_STATVFS
28 #include <sys/statvfs.h>
31 # if STATFS_IN_SYS_VFS
34 # if STATFS_IN_SYS_MOUNT
35 # include <sys/mount.h>
37 # if STATFS_IN_SYS_STATFS
38 # include <sys/statfs.h>
50 char * hGetNEVR(Header h, const char ** np)
52 const char * n, * v, * r;
55 (void) headerNVR(h, &n, &v, &r);
56 NVR = t = xcalloc(1, strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
67 char * hGetNEVRA(Header h, const char ** np)
69 const char * n, * v, * r, * a;
73 (void) headerNVR(h, &n, &v, &r);
74 xx = headerGetEntry(h, RPMTAG_ARCH, NULL, (void **) &a, NULL);
75 NVRA = t = xcalloc(1, strlen(n) + strlen(v) + strlen(r) + strlen(a) + sizeof("--."));
88 uint32_t hGetColor(Header h)
90 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
98 if (hge(h, RPMTAG_FILECOLORS, NULL, (void **)&fcolors, &ncolors)
99 && fcolors != NULL && ncolors > 0)
101 for (i = 0; i < ncolors; i++)
102 hcolor |= fcolors[i];
109 rpmts XrpmtsUnlink(rpmts ts, const char * msg, const char * fn, unsigned ln)
112 fprintf(stderr, "--> ts %p -- %d %s at %s:%u\n", ts, ts->nrefs, msg, fn, ln);
117 rpmts XrpmtsLink(rpmts ts, const char * msg, const char * fn, unsigned ln)
121 fprintf(stderr, "--> ts %p ++ %d %s at %s:%u\n", ts, ts->nrefs, msg, fn, ln);
125 int rpmtsCloseDB(rpmts ts)
129 if (ts->rdb != NULL) {
130 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET),
131 rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
132 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
133 rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
134 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
135 rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
136 rc = rpmdbClose(ts->rdb);
142 int rpmtsOpenDB(rpmts ts, int dbmode)
146 if (ts->rdb != NULL && ts->dbmode == dbmode)
149 (void) rpmtsCloseDB(ts);
151 /* XXX there's a potential db lock race here. */
154 rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
157 dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
159 _("cannot open Packages database in %s\n"), dn);
165 int rpmtsInitDB(rpmts ts, int dbmode)
167 void *lock = rpmtsAcquireLock(ts);
170 rc = rpmdbInit(ts->rootDir, dbmode);
175 int rpmtsGetDBMode(rpmts ts)
181 int rpmtsSetDBMode(rpmts ts, int dbmode)
184 /* mode setting only permitted on non-open db */
185 if (ts != NULL && rpmtsGetRdb(ts) == NULL) {
193 int rpmtsRebuildDB(rpmts ts)
196 void *lock = rpmtsAcquireLock(ts);
197 if (!lock) return -1;
198 if (!(ts->vsflags & RPMVSF_NOHDRCHK))
199 rc = rpmdbRebuild(ts->rootDir, ts, headerCheck);
201 rc = rpmdbRebuild(ts->rootDir, NULL, NULL);
206 int rpmtsVerifyDB(rpmts ts)
208 return rpmdbVerify(ts->rootDir);
211 static int isArch(const char * arch)
214 static const char *arches[] = {
215 "i386", "i486", "i586", "i686", "athlon", "pentium3", "pentium4", "x86_64", "amd64", "ia32e",
216 "alpha", "alphaev5", "alphaev56", "alphapca56", "alphaev6", "alphaev67",
217 "sparc", "sun4", "sun4m", "sun4c", "sun4d", "sparcv8", "sparcv9", "sparcv9v",
218 "sparc64", "sparc64v", "sun4u",
219 "mips", "mipsel", "IP",
220 "ppc", "ppciseries", "ppcpseries",
221 "ppc64", "ppc64iseries", "ppc64pseries",
225 "armv3l", "armv4b", "armv4l", "armv4tl", "armv5tel", "armv5tejl", "armv6l",
226 "s390", "i370", "s390x",
232 for (av = arches; *av != NULL; av++) {
233 if (!strcmp(arch, *av))
239 /* keyp might no be defined. */
240 rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmTag rpmtag,
241 const void * keyp, size_t keylen)
243 rpmdbMatchIterator mi;
244 const char * arch = NULL;
247 if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
250 /* Parse out "N(EVR).A" tokens from a label key. */
251 if (rpmtag == RPMDBI_LABEL && keyp != NULL) {
252 const char * s = keyp;
254 size_t slen = strlen(s);
255 char *t = alloca(slen+1);
260 while ((c = *s++) != '\0') {
266 /* XXX Fail if nested parens. */
268 rpmlog(RPMLOG_ERR, _("extra '(' in package label: %s\n"), keyp);
271 /* Parse explicit epoch. */
272 for (se = s; *se && xisdigit(*se); se++)
275 /* XXX skip explicit epoch's (for now) */
279 /* No Epoch: found. Convert '(' to '-' and chug. */
284 /* XXX Fail if nested parens. */
286 rpmlog(RPMLOG_ERR, _("missing '(' in package label: %s\n"), keyp);
289 /* Don't copy trailing ')' */
294 rpmlog(RPMLOG_ERR, _("missing ')' in package label: %s\n"), keyp);
300 /* Is this a valid ".arch" suffix? */
301 if (t != NULL && isArch(t+1)) {
307 mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
309 /* Verify header signature/digest during retrieve (if not disabled). */
310 if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
311 (void) rpmdbSetHdrChk(mi, ts, headerCheck);
313 /* Select specified arch only. */
315 xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch);
319 rpmRC rpmtsFindPubkey(rpmts ts)
321 const void * sig = rpmtsSig(ts);
322 pgpDig dig = rpmtsDig(ts);
323 pgpDigParams sigp = rpmtsSignature(ts);
324 pgpDigParams pubp = rpmtsPubkey(ts);
325 rpmRC res = RPMRC_NOKEY;
326 const char * pubkeysource = NULL;
329 if (sig == NULL || dig == NULL || sigp == NULL || pubp == NULL)
333 fprintf(stderr, "==> find sig id %08x %08x ts pubkey id %08x %08x\n",
334 pgpGrab(sigp->signid, 4), pgpGrab(sigp->signid+4, 4),
335 pgpGrab(ts->pksignid, 4), pgpGrab(ts->pksignid+4, 4));
338 /* Lazy free of previous pubkey if pubkey does not match this signature. */
339 if (memcmp(sigp->signid, ts->pksignid, sizeof(ts->pksignid))) {
341 fprintf(stderr, "*** free pkt %p[%d] id %08x %08x\n", ts->pkpkt, ts->pkpktlen, pgpGrab(ts->pksignid, 4), pgpGrab(ts->pksignid+4, 4));
343 ts->pkpkt = _free(ts->pkpkt);
345 memset(ts->pksignid, 0, sizeof(ts->pksignid));
348 /* Try rpmdb keyring lookup. */
349 if (ts->pkpkt == NULL) {
352 rpmdbMatchIterator mi;
355 /* Retrieve the pubkey that matches the signature. */
356 mi = rpmtsInitIterator(ts, RPMTAG_PUBKEYS, sigp->signid, sizeof(sigp->signid));
357 while ((h = rpmdbNextIterator(mi)) != NULL) {
358 const char ** pubkeys;
361 if (!headerGetEntry(h, RPMTAG_PUBKEYS, &pt, (void **)&pubkeys, &pc))
363 hx = rpmdbGetIteratorOffset(mi);
364 ix = rpmdbGetIteratorFileNum(mi);
366 || b64decode(pubkeys[ix], (void **) &ts->pkpkt, &ts->pkpktlen))
368 pubkeys = headerFreeData(pubkeys, pt);
371 mi = rpmdbFreeIterator(mi);
375 sprintf(hnum, "h#%d", hx);
376 pubkeysource = xstrdup(hnum);
378 ts->pkpkt = _free(ts->pkpkt);
383 /* Try keyserver lookup. */
384 if (ts->pkpkt == NULL) {
385 const char * fn = rpmExpand("%{_hkp_keyserver_query}",
386 pgpHexStr(sigp->signid, sizeof(sigp->signid)), NULL);
389 if (fn && *fn != '%') {
390 xx = (pgpReadPkts(fn,&ts->pkpkt,&ts->pkpktlen) != PGPARMOR_PUBKEY);
394 ts->pkpkt = _free(ts->pkpkt);
397 /* Save new pubkey in local ts keyring for delayed import. */
398 pubkeysource = xstrdup("keyserver");
403 /* Try filename from macro lookup. */
404 if (ts->pkpkt == NULL) {
405 const char * fn = rpmExpand("%{_gpg_pubkey}", NULL);
408 if (fn && *fn != '%')
409 xx = (pgpReadPkts(fn,&ts->pkpkt,&ts->pkpktlen) != PGPARMOR_PUBKEY);
412 ts->pkpkt = _free(ts->pkpkt);
415 pubkeysource = xstrdup("macro");
420 /* Was a matching pubkey found? */
421 if (ts->pkpkt == NULL || ts->pkpktlen == 0)
424 /* Retrieve parameters from pubkey packet(s). */
425 xx = pgpPrtPkts(ts->pkpkt, ts->pkpktlen, dig, 0);
427 /* Do the parameters match the signature? */
428 if (sigp->pubkey_algo == pubp->pubkey_algo
430 && sigp->hash_algo == pubp->hash_algo
432 && !memcmp(sigp->signid, pubp->signid, sizeof(sigp->signid)) )
435 /* XXX Verify any pubkey signatures. */
437 /* Pubkey packet looks good, save the signer id. */
438 memcpy(ts->pksignid, pubp->signid, sizeof(ts->pksignid));
441 rpmlog(RPMLOG_DEBUG, "========== %s pubkey id %08x %08x (%s)\n",
442 (sigp->pubkey_algo == PGPPUBKEYALGO_DSA ? "DSA" :
443 (sigp->pubkey_algo == PGPPUBKEYALGO_RSA ? "RSA" : "???")),
444 pgpGrab(sigp->signid, 4), pgpGrab(sigp->signid+4, 4),
451 pubkeysource = _free(pubkeysource);
452 if (res != RPMRC_OK) {
453 ts->pkpkt = _free(ts->pkpkt);
459 rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, ssize_t pktlen)
461 static unsigned char zeros[] =
462 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
463 const char * afmt = "%{pubkeys:armor}";
464 const char * group = "Public Keys";
465 const char * license = "pubkey";
466 const char * buildhost = "localhost";
467 int32_t pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
470 pgpDigParams pubp = NULL;
471 const char * d = NULL;
472 const char * enc = NULL;
473 const char * n = NULL;
474 const char * u = NULL;
475 const char * v = NULL;
476 const char * r = NULL;
477 const char * evr = NULL;
479 rpmRC rc = RPMRC_FAIL; /* assume failure */
483 if (pkt == NULL || pktlen <= 0)
485 if (rpmtsOpenDB(ts, (O_RDWR|O_CREAT)))
488 if ((enc = b64encode(pkt, pktlen, -1)) == NULL)
493 /* Build header elements. */
494 (void) pgpPrtPkts(pkt, pktlen, dig, 0);
497 if (!memcmp(pubp->signid, zeros, sizeof(pubp->signid))
498 || !memcmp(pubp->time, zeros, sizeof(pubp->time))
499 || pubp->userid == NULL)
502 v = t = xmalloc(16+1);
503 t = stpcpy(t, pgpHexStr(pubp->signid, sizeof(pubp->signid)));
505 r = t = xmalloc(8+1);
506 t = stpcpy(t, pgpHexStr(pubp->time, sizeof(pubp->time)));
508 n = t = xmalloc(sizeof("gpg()")+8);
509 t = stpcpy( stpcpy( stpcpy(t, "gpg("), v+8), ")");
511 /* FIX: pubp->userid may be NULL */
512 u = t = xmalloc(sizeof("gpg()")+strlen(pubp->userid));
513 t = stpcpy( stpcpy( stpcpy(t, "gpg("), pubp->userid), ")");
515 evr = t = xmalloc(sizeof("4X:-")+strlen(v)+strlen(r));
516 t = stpcpy(t, (pubp->version == 4 ? "4:" : "3:"));
517 t = stpcpy( stpcpy( stpcpy(t, v), "-"), r);
519 /* Check for pre-existing header. */
521 /* Build pubkey header. */
524 xx = headerAddOrAppendEntry(h, RPMTAG_PUBKEYS,
525 RPM_STRING_ARRAY_TYPE, &enc, 1);
527 d = headerSprintf(h, afmt, rpmTagTable, rpmHeaderFormats, NULL);
531 xx = headerAddEntry(h, RPMTAG_NAME, RPM_STRING_TYPE, "gpg-pubkey", 1);
532 xx = headerAddEntry(h, RPMTAG_VERSION, RPM_STRING_TYPE, v+8, 1);
533 xx = headerAddEntry(h, RPMTAG_RELEASE, RPM_STRING_TYPE, r, 1);
534 xx = headerAddEntry(h, RPMTAG_DESCRIPTION, RPM_STRING_TYPE, d, 1);
535 xx = headerAddEntry(h, RPMTAG_GROUP, RPM_STRING_TYPE, group, 1);
536 xx = headerAddEntry(h, RPMTAG_LICENSE, RPM_STRING_TYPE, license, 1);
537 xx = headerAddEntry(h, RPMTAG_SUMMARY, RPM_STRING_TYPE, u, 1);
539 xx = headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE, &zero, 1);
541 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME,
542 RPM_STRING_ARRAY_TYPE, &u, 1);
543 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION,
544 RPM_STRING_ARRAY_TYPE, &evr, 1);
545 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS,
546 RPM_INT32_TYPE, &pflags, 1);
548 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME,
549 RPM_STRING_ARRAY_TYPE, &n, 1);
550 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION,
551 RPM_STRING_ARRAY_TYPE, &evr, 1);
552 xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS,
553 RPM_INT32_TYPE, &pflags, 1);
555 xx = headerAddEntry(h, RPMTAG_RPMVERSION, RPM_STRING_TYPE, RPMVERSION, 1);
557 /* XXX W2DO: tag value inheirited from parent? */
558 xx = headerAddEntry(h, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildhost, 1);
559 { int32_t tid = rpmtsGetTid(ts);
560 xx = headerAddEntry(h, RPMTAG_INSTALLTIME, RPM_INT32_TYPE, &tid, 1);
561 /* XXX W2DO: tag value inheirited from parent? */
562 xx = headerAddEntry(h, RPMTAG_BUILDTIME, RPM_INT32_TYPE, &tid, 1);
566 /* XXX W2DO: tag value inheirited from parent? */
567 xx = headerAddEntry(h, RPMTAG_SOURCERPM, RPM_STRING_TYPE, fn, 1);
570 /* Add header to database. */
571 xx = rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h, NULL, NULL);
579 dig = pgpFreeDig(dig);
591 int rpmtsCloseSDB(rpmts ts)
595 if (ts->sdb != NULL) {
596 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET),
597 rpmdbOp(ts->sdb, RPMDB_OP_DBGET));
598 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
599 rpmdbOp(ts->sdb, RPMDB_OP_DBPUT));
600 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
601 rpmdbOp(ts->sdb, RPMDB_OP_DBDEL));
602 rc = rpmdbClose(ts->sdb);
608 int rpmtsOpenSDB(rpmts ts, int dbmode)
610 static int has_sdbpath = -1;
613 if (ts->sdb != NULL && ts->sdbmode == dbmode)
617 has_sdbpath = rpmExpandNumeric("%{?_solve_dbpath:1}");
619 /* If not configured, don't try to open. */
620 if (has_sdbpath <= 0)
623 addMacro(NULL, "_dbpath", NULL, "%{_solve_dbpath}", RMIL_DEFAULT);
625 rc = rpmdbOpen(ts->rootDir, &ts->sdb, ts->sdbmode, 0644);
628 dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
629 rpmlog(RPMLOG_WARNING,
630 _("cannot open Solve database in %s\n"), dn);
633 delMacro(NULL, "_dbpath");
639 * Compare suggested package resolutions (qsort/bsearch).
640 * @param a 1st instance address
641 * @param b 2nd instance address
642 * @return result of comparison
644 static int sugcmp(const void * a, const void * b)
646 const char * astr = *(const char **)a;
647 const char * bstr = *(const char **)b;
648 return strcmp(astr, bstr);
651 int rpmtsSolve(rpmts ts, rpmds ds, const void * data)
656 rpmdbMatchIterator mi;
664 int rc = 1; /* assume not found */
667 if (rpmdsTagN(ds) != RPMTAG_REQUIRENAME)
674 if (ts->sdb == NULL) {
675 xx = rpmtsOpenSDB(ts, ts->sdbmode);
679 /* Look for a matching Provides: in suggested universe. */
680 rpmtag = (*keyp == '/' ? RPMTAG_BASENAMES : RPMTAG_PROVIDENAME);
682 mi = rpmdbInitIterator(ts->sdb, rpmtag, keyp, keylen);
686 while ((h = rpmdbNextIterator(mi)) != NULL) {
692 if (rpmtag == RPMTAG_PROVIDENAME && !rpmdsAnyMatchesDep(h, ds, 1))
695 /* XXX Prefer the shortest name if given alternatives. */
698 if (headerGetEntry(h, RPMTAG_NAME, NULL, (void **)&hname, NULL)) {
700 hnamelen = strlen(hname);
702 if (bhnamelen > 0 && hnamelen > bhnamelen)
705 /* XXX Prefer the newest build if given alternatives. */
707 if (headerGetEntry(h, RPMTAG_BUILDTIME, NULL, (void **)&ip, NULL))
716 bhnamelen = hnamelen;
718 mi = rpmdbFreeIterator(mi);
720 /* Is there a suggested resolution? */
724 /* Format the suggestion. */
725 qfmt = rpmExpand("%{?_solve_name_fmt}", NULL);
726 if (qfmt == NULL || *qfmt == '\0')
728 str = headerSprintf(bh, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
732 rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
736 if (ts->transFlags & RPMTRANS_FLAG_ADDINDEPS) {
741 fd = Fopen(str, "r.ufdio");
742 if (fd == NULL || Ferror(fd)) {
743 rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), str,
752 rpmrc = rpmReadPackageFile(ts, fd, str, &h);
758 case RPMRC_NOTTRUSTED:
762 !rpmtsAddInstallElement(ts, h, (fnpyKey)str, 1, NULL))
764 rpmlog(RPMLOG_DEBUG, _("Adding: %s\n"), str);
766 /* XXX str memory leak */
776 rpmlog(RPMLOG_DEBUG, _("Suggesting: %s\n"), str);
777 /* If suggestion is already present, don't bother. */
778 if (ts->suggests != NULL && ts->nsuggests > 0) {
779 if (bsearch(&str, ts->suggests, ts->nsuggests,
780 sizeof(*ts->suggests), sugcmp))
784 /* Add a new (unique) suggestion. */
785 ts->suggests = xrealloc(ts->suggests,
786 sizeof(*ts->suggests) * (ts->nsuggests + 2));
787 ts->suggests[ts->nsuggests] = str;
789 ts->suggests[ts->nsuggests] = NULL;
791 if (ts->nsuggests > 1)
792 qsort(ts->suggests, ts->nsuggests, sizeof(*ts->suggests), sugcmp);
795 /* FIX: ts->suggests[] may be NULL */
799 int rpmtsAvailable(rpmts ts, const rpmds ds)
802 int rc = 1; /* assume not found */
804 if (ts->availablePackages == NULL)
806 sugkey = rpmalAllSatisfiesDepend(ts->availablePackages, ds, NULL);
810 /* XXX no alternatives yet */
811 if (sugkey[0] != NULL) {
812 ts->suggests = xrealloc(ts->suggests,
813 sizeof(*ts->suggests) * (ts->nsuggests + 2));
814 ts->suggests[ts->nsuggests] = sugkey[0];
817 ts->suggests[ts->nsuggests] = NULL;
819 sugkey = _free(sugkey);
820 /* FIX: ts->suggests[] may be NULL */
824 int rpmtsSetSolveCallback(rpmts ts,
825 int (*solve) (rpmts ts, rpmds key, const void * data),
826 const void * solveData)
832 ts->solveData = solveData;
837 void rpmtsPrintSuggests(rpmts ts)
839 if (ts->suggests != NULL && ts->nsuggests > 0) {
841 rpmlog(RPMLOG_NOTICE, _(" Suggested resolutions:\n"));
842 for (i = 0; i < ts->nsuggests; i++) {
843 const char * str = ts->suggests[i];
848 rpmlog(RPMLOG_NOTICE, "\t%s\n", str);
854 rpmps rpmtsProblems(rpmts ts)
859 ps = rpmpsLink(ts->probs, NULL);
864 void rpmtsCleanProblems(rpmts ts)
866 if (ts && ts->probs) {
867 ts->probs = rpmpsFree(ts->probs);
871 void rpmtsCleanDig(rpmts ts)
873 ts->sig = headerFreeData(ts->sig, ts->sigtype);
874 ts->dig = pgpFreeDig(ts->dig);
877 void rpmtsClean(rpmts ts)
885 /* Clean up after dependency checks. */
887 while ((p = rpmtsiNext(pi, 0)) != NULL)
891 ts->addedPackages = rpmalFree(ts->addedPackages);
892 ts->numAddedPackages = 0;
894 for (i = 0; i < ts->nsuggests; i++) {
895 const char * str = ts->suggests[i];
896 ts->suggests[i] = NULL;
899 ts->suggests = _free(ts->suggests);
902 rpmtsCleanProblems(ts);
907 void rpmtsEmpty(rpmts ts)
917 for (pi = rpmtsiInit(ts), oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
918 ts->order[oc] = rpmteFree(ts->order[oc]);
926 ts->numRemovedPackages = 0;
930 static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
932 static unsigned int scale = (1000 * 1000);
933 if (op != NULL && op->count > 0)
934 fprintf(stderr, " %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
936 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
937 op->usecs/scale, op->usecs%scale);
940 static void rpmtsPrintStats(rpmts ts)
942 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
944 rpmtsPrintStat("total: ", rpmtsOp(ts, RPMTS_OP_TOTAL));
945 rpmtsPrintStat("check: ", rpmtsOp(ts, RPMTS_OP_CHECK));
946 rpmtsPrintStat("order: ", rpmtsOp(ts, RPMTS_OP_ORDER));
947 rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
948 rpmtsPrintStat("repackage: ", rpmtsOp(ts, RPMTS_OP_REPACKAGE));
949 rpmtsPrintStat("install: ", rpmtsOp(ts, RPMTS_OP_INSTALL));
950 rpmtsPrintStat("erase: ", rpmtsOp(ts, RPMTS_OP_ERASE));
951 rpmtsPrintStat("scriptlets: ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
952 rpmtsPrintStat("compress: ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
953 rpmtsPrintStat("uncompress: ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
954 rpmtsPrintStat("digest: ", rpmtsOp(ts, RPMTS_OP_DIGEST));
955 rpmtsPrintStat("signature: ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
956 rpmtsPrintStat("dbadd: ", rpmtsOp(ts, RPMTS_OP_DBADD));
957 rpmtsPrintStat("dbremove: ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
958 rpmtsPrintStat("dbget: ", rpmtsOp(ts, RPMTS_OP_DBGET));
959 rpmtsPrintStat("dbput: ", rpmtsOp(ts, RPMTS_OP_DBPUT));
960 rpmtsPrintStat("dbdel: ", rpmtsOp(ts, RPMTS_OP_DBDEL));
963 rpmts rpmtsFree(rpmts ts)
969 return rpmtsUnlink(ts, "tsCreate");
973 (void) rpmtsCloseDB(ts);
975 (void) rpmtsCloseSDB(ts);
977 ts->removedPackages = _free(ts->removedPackages);
979 ts->availablePackages = rpmalFree(ts->availablePackages);
980 ts->numAvailablePackages = 0;
982 ts->dsi = _free(ts->dsi);
984 if (ts->scriptFd != NULL) {
985 ts->scriptFd = fdFree(ts->scriptFd, "rpmtsFree");
988 ts->rootDir = _free(ts->rootDir);
989 ts->currDir = _free(ts->currDir);
991 ts->order = _free(ts->order);
992 ts->orderAlloced = 0;
994 if (ts->pkpkt != NULL)
995 ts->pkpkt = _free(ts->pkpkt);
997 memset(ts->pksignid, 0, sizeof(ts->pksignid));
1000 rpmtsPrintStats(ts);
1002 /* Free up the memory used by the rpmtsScore */
1003 ts->score = rpmtsScoreFree(ts->score);
1005 (void) rpmtsUnlink(ts, "tsCreate");
1012 rpmVSFlags rpmtsVSFlags(rpmts ts)
1014 rpmVSFlags vsflags = 0;
1016 vsflags = ts->vsflags;
1020 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
1022 rpmVSFlags ovsflags = 0;
1024 ovsflags = ts->vsflags;
1025 ts->vsflags = vsflags;
1031 * This allows us to mark transactions as being of a certain type.
1032 * The three types are:
1035 * RPM_TRANS_ROLLBACK
1036 * RPM_TRANS_AUTOROLLBACK
1038 * ROLLBACK and AUTOROLLBACK transactions should always be ran as
1039 * a best effort. In particular this is important to the autorollback
1040 * feature to avoid rolling back a rollback (otherwise known as
1041 * dueling rollbacks (-;). AUTOROLLBACK's additionally need instance
1042 * counts passed to scriptlets to be altered.
1044 void rpmtsSetType(rpmts ts, rpmtsType type)
1051 /* Let them know what type of transaction we are */
1052 rpmtsType rpmtsGetType(rpmts ts)
1060 int rpmtsUnorderedSuccessors(rpmts ts, int first)
1062 int unorderedSuccessors = 0;
1064 unorderedSuccessors = ts->unorderedSuccessors;
1066 ts->unorderedSuccessors = first;
1068 return unorderedSuccessors;
1071 const char * rpmtsRootDir(rpmts ts)
1073 const char * rootDir = NULL;
1075 if (ts != NULL && ts->rootDir != NULL) {
1076 urltype ut = urlPath(ts->rootDir, &rootDir);
1078 case URL_IS_UNKNOWN:
1081 /* XXX these shouldn't be allowed as rootdir! */
1095 void rpmtsSetRootDir(rpmts ts, const char * rootDir)
1100 ts->rootDir = _free(ts->rootDir);
1102 if (rootDir == NULL) {
1104 ts->rootDir = xstrdup("");
1108 rootLen = strlen(rootDir);
1110 /* Make sure that rootDir has trailing / */
1111 if (!(rootLen && rootDir[rootLen - 1] == '/')) {
1112 char * t = alloca(rootLen + 2);
1114 (void) stpcpy( stpcpy(t, rootDir), "/");
1117 ts->rootDir = xstrdup(rootDir);
1121 const char * rpmtsCurrDir(rpmts ts)
1123 const char * currDir = NULL;
1125 currDir = ts->currDir;
1130 void rpmtsSetCurrDir(rpmts ts, const char * currDir)
1133 ts->currDir = _free(ts->currDir);
1135 ts->currDir = xstrdup(currDir);
1139 FD_t rpmtsScriptFd(rpmts ts)
1141 FD_t scriptFd = NULL;
1143 scriptFd = ts->scriptFd;
1148 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
1152 if (ts->scriptFd != NULL) {
1153 ts->scriptFd = fdFree(ts->scriptFd, "rpmtsSetScriptFd");
1154 ts->scriptFd = NULL;
1156 if (scriptFd != NULL)
1157 ts->scriptFd = fdLink((void *)scriptFd, "rpmtsSetScriptFd");
1161 int rpmtsSELinuxEnabled(rpmts ts)
1163 return (ts != NULL ? (ts->selinuxEnabled > 0) : 0);
1166 int rpmtsChrootDone(rpmts ts)
1168 return (ts != NULL ? ts->chrootDone : 0);
1171 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
1173 int ochrootDone = 0;
1175 ochrootDone = ts->chrootDone;
1176 rpmdbSetChrootDone(rpmtsGetRdb(ts), chrootDone);
1177 ts->chrootDone = chrootDone;
1182 int32_t rpmtsGetTid(rpmts ts)
1191 int32_t rpmtsSetTid(rpmts ts, int32_t tid)
1201 int32_t rpmtsSigtag(const rpmts ts)
1205 sigtag = ts->sigtag;
1209 int32_t rpmtsSigtype(const rpmts ts)
1211 int32_t sigtype = 0;
1213 sigtype = ts->sigtype;
1217 const void * rpmtsSig(const rpmts ts)
1219 const void * sig = NULL;
1225 int32_t rpmtsSiglen(const rpmts ts)
1229 siglen = ts->siglen;
1233 int rpmtsSetSig(rpmts ts,
1234 int32_t sigtag, int32_t sigtype, const void * sig, int32_t siglen)
1237 if (ts->sig && ts->sigtype)
1238 ts->sig = headerFreeData(ts->sig, ts->sigtype);
1239 ts->sigtag = sigtag;
1240 ts->sigtype = (sig ? sigtype : 0);
1242 ts->siglen = siglen;
1247 pgpDig rpmtsDig(rpmts ts)
1249 /* FIX: hide lazy malloc for now */
1250 if (ts->dig == NULL)
1251 ts->dig = pgpNewDig();
1252 if (ts->dig == NULL)
1257 pgpDigParams rpmtsSignature(const rpmts ts)
1259 pgpDig dig = rpmtsDig(ts);
1260 if (dig == NULL) return NULL;
1261 return &dig->signature;
1264 pgpDigParams rpmtsPubkey(const rpmts ts)
1266 pgpDig dig = rpmtsDig(ts);
1267 if (dig == NULL) return NULL;
1268 return &dig->pubkey;
1271 rpmdb rpmtsGetRdb(rpmts ts)
1280 int rpmtsInitDSI(const rpmts ts)
1282 rpmDiskSpaceInfo dsi;
1287 if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
1290 rpmlog(RPMLOG_DEBUG, _("mounted filesystems:\n"));
1291 rpmlog(RPMLOG_DEBUG,
1292 _(" i dev bsize bavail iavail mount point\n"));
1294 rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
1295 if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
1298 /* Get available space on mounted file systems. */
1300 ts->dsi = _free(ts->dsi);
1301 ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
1306 for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
1307 #if STATFS_IN_SYS_STATVFS
1309 memset(&sfb, 0, sizeof(sfb));
1310 rc = statvfs(ts->filesystems[i], &sfb);
1313 memset(&sfb, 0, sizeof(sfb));
1315 /* This platform has the 4-argument version of the statfs call. The last two
1316 * should be the size of struct statfs and 0, respectively. The 0 is the
1317 * filesystem type, and is always 0 when statfs is called on a mounted
1318 * filesystem, as we're doing.
1320 rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
1322 rc = statfs(ts->filesystems[i], &sfb);
1328 rc = stat(ts->filesystems[i], &sb);
1331 dsi->dev = sb.st_dev;
1333 dsi->bsize = sfb.f_bsize;
1336 #ifdef STATFS_HAS_F_BAVAIL
1337 dsi->bavail = sfb.f_bavail;
1339 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1340 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1341 * it's about all we can do.
1343 dsi->bavail = sfb.f_blocks - sfb.f_bfree;
1345 /* XXX Avoid FAT and other file systems that have not inodes. */
1346 dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1348 rpmlog(RPMLOG_DEBUG, _("%5d 0x%08x %8u %12ld %12ld %s\n"),
1349 i, (unsigned) dsi->dev, (unsigned) dsi->bsize,
1350 (signed long) dsi->bavail, (signed long) dsi->iavail,
1351 ts->filesystems[i]);
1356 void rpmtsUpdateDSI(const rpmts ts, dev_t dev,
1357 uint32_t fileSize, uint32_t prevSize, uint32_t fixupSize,
1358 rpmFileAction action)
1360 rpmDiskSpaceInfo dsi;
1365 while (dsi->bsize && dsi->dev != dev)
1367 if (dsi->bsize == 0)
1373 bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
1380 dsi->bneeded += bneeded;
1384 * FIXME: If two packages share a file (same md5sum), and
1385 * that file is being replaced on disk, will dsi->bneeded get
1386 * adjusted twice? Quite probably!
1389 dsi->bneeded += bneeded;
1390 dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
1395 dsi->bneeded -= bneeded;
1403 dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
1406 void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
1408 rpmDiskSpaceInfo dsi;
1413 if (ts->filesystems == NULL || ts->filesystemCount <= 0)
1419 fc = rpmfiFC( rpmteFI(te, RPMTAG_BASENAMES) );
1423 ps = rpmtsProblems(ts);
1424 for (i = 0; i < ts->filesystemCount; i++, dsi++) {
1426 if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
1427 rpmpsAppend(ps, RPMPROB_DISKSPACE,
1428 rpmteNEVRA(te), rpmteKey(te),
1429 ts->filesystems[i], NULL, NULL,
1430 (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
1433 if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
1434 rpmpsAppend(ps, RPMPROB_DISKNODES,
1435 rpmteNEVRA(te), rpmteKey(te),
1436 ts->filesystems[i], NULL, NULL,
1437 (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
1443 void * rpmtsNotify(rpmts ts, rpmte te,
1444 rpmCallbackType what, unsigned long amount, unsigned long total)
1447 if (ts && ts->notify && te) {
1448 Header h = rpmteHeader(te);
1449 assert(!(rpmteType(te) == TR_ADDED && h == NULL));
1452 ptr = ts->notify(h, what, amount, total,
1453 rpmteKey(te), ts->notifyData);
1454 headerUnlink(h); /* undo rpmteHeader() ref */
1459 int rpmtsNElements(rpmts ts)
1462 if (ts != NULL && ts->order != NULL) {
1463 nelements = ts->orderCount;
1468 rpmte rpmtsElement(rpmts ts, int ix)
1471 if (ts != NULL && ts->order != NULL) {
1472 if (ix >= 0 && ix < ts->orderCount)
1478 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
1480 return (ts != NULL ? ts->ignoreSet : 0);
1483 rpmtransFlags rpmtsFlags(rpmts ts)
1485 return (ts != NULL ? ts->transFlags : 0);
1488 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
1490 rpmtransFlags otransFlags = 0;
1492 otransFlags = ts->transFlags;
1493 ts->transFlags = transFlags;
1498 rpmSpec rpmtsSpec(rpmts ts)
1503 rpmSpec rpmtsSetSpec(rpmts ts, rpmSpec spec)
1505 rpmSpec ospec = ts->spec;
1510 rpmte rpmtsRelocateElement(rpmts ts)
1512 return ts->relocateElement;
1515 rpmte rpmtsSetRelocateElement(rpmts ts, rpmte relocateElement)
1517 rpmte orelocateElement = ts->relocateElement;
1518 ts->relocateElement = relocateElement;
1519 return orelocateElement;
1522 uint32_t rpmtsColor(rpmts ts)
1524 return (ts != NULL ? ts->color : 0);
1527 uint32_t rpmtsSetColor(rpmts ts, uint32_t color)
1529 uint32_t ocolor = 0;
1537 uint32_t rpmtsPrefColor(rpmts ts)
1539 return (ts != NULL ? ts->prefcolor : 0);
1542 rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
1546 if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
1551 int rpmtsSetNotifyCallback(rpmts ts,
1552 rpmCallbackFunction notify, rpmCallbackData notifyData)
1555 ts->notify = notify;
1556 ts->notifyData = notifyData;
1561 int rpmtsGetKeys(const rpmts ts, fnpyKey ** ep, int * nep)
1565 if (nep) *nep = ts->orderCount;
1570 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
1571 pi = rpmtsiInit(ts);
1572 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1573 switch (rpmteType(p)) {
1584 pi = rpmtsiFree(pi);
1589 rpmts rpmtsCreate(void)
1593 ts = xcalloc(1, sizeof(*ts));
1594 memset(&ts->ops, 0, sizeof(ts->ops));
1595 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
1596 ts->type = RPMTRANS_TYPE_NORMAL;
1597 ts->filesystemCount = 0;
1598 ts->filesystems = NULL;
1601 ts->solve = rpmtsSolve;
1602 ts->solveData = NULL;
1604 ts->suggests = NULL;
1606 ts->sdbmode = O_RDONLY;
1609 ts->dbmode = O_RDONLY;
1611 ts->scriptFd = NULL;
1612 ts->tid = (int32_t) time(NULL);
1615 ts->color = rpmExpandNumeric("%{?_transaction_color}");
1616 ts->prefcolor = rpmExpandNumeric("%{?_prefer_color}")?:2;
1618 ts->numRemovedPackages = 0;
1619 ts->allocedRemovedPackages = ts->delta;
1620 ts->removedPackages = xcalloc(ts->allocedRemovedPackages,
1621 sizeof(*ts->removedPackages));
1627 ts->selinuxEnabled = is_selinux_enabled();
1629 ts->numAddedPackages = 0;
1630 ts->addedPackages = NULL;
1632 ts->numAvailablePackages = 0;
1633 ts->availablePackages = NULL;
1635 ts->orderAlloced = 0;
1646 memset(ts->pksignid, 0, sizeof(ts->pksignid));
1650 * We only use the score in an autorollback. So set this to
1657 return rpmtsLink(ts, "tsCreate");
1660 /**********************
1661 * Transaction Scores *
1662 **********************/
1665 rpmRC rpmtsScoreInit(rpmts runningTS, rpmts rollbackTS)
1671 int tranElements; /* Number of transaction elements in runningTS */
1673 rpmRC rc = RPMRC_OK; /* Assume success */
1676 rpmlog(RPMLOG_DEBUG, _("Creating transaction score board(%p, %p)\n"),
1677 runningTS, rollbackTS);
1679 /* Allocate space for score board */
1680 score = xcalloc(1, sizeof(*score));
1681 rpmlog(RPMLOG_DEBUG, _("\tScore board address: %p\n"), score);
1684 * Determine the maximum size needed for the entry list.
1685 * XXX: Today, I just get the count of rpmts elements, and allocate
1686 * an array that big. Yes this is guaranteed to waste memory.
1687 * Future updates will hopefully make this more efficient,
1688 * but for now it will work.
1690 tranElements = rpmtsNElements(runningTS);
1691 rpmlog(RPMLOG_DEBUG, _("\tAllocating space for %d entries\n"), tranElements);
1692 score->scores = xcalloc(tranElements, sizeof(score->scores));
1694 /* Initialize score entry count */
1699 * Increment through transaction elements and make sure for every
1700 * N there is an rpmtsScoreEntry.
1702 pi = rpmtsiInit(runningTS);
1703 while ((p = rpmtsiNext(pi, TR_ADDED|TR_REMOVED)) != NULL) {
1706 /* Try to find the entry in the score list */
1707 for(i = 0; i < score->entries; i++) {
1708 se = score->scores[i];
1709 if (strcmp(rpmteN(p), se->N) == 0) {
1715 /* If we did not find the entry then allocate space for it */
1717 /* XXX p->fi->te undefined. */
1718 rpmlog(RPMLOG_DEBUG, _("\tAdding entry for %s to score board.\n"),
1720 se = xcalloc(1, sizeof(*(*(score->scores))));
1721 rpmlog(RPMLOG_DEBUG, _("\t\tEntry address: %p\n"), se);
1722 se->N = xstrdup(rpmteN(p));
1723 se->te_types = rpmteType(p);
1726 score->scores[score->entries] = se;
1729 /* We found this one, so just add the element type to the one
1732 rpmlog(RPMLOG_DEBUG, _("\tUpdating entry for %s in score board.\n"),
1734 score->scores[i]->te_types |= rpmteType(p);
1738 pi = rpmtsiFree(pi);
1741 * Attach the score to the running transaction and the autorollback
1744 runningTS->score = score;
1746 rollbackTS->score = score;
1752 rpmtsScore rpmtsScoreFree(rpmtsScore score)
1754 rpmtsScoreEntry se = NULL;
1757 rpmlog(RPMLOG_DEBUG, _("May free Score board(%p)\n"), score);
1759 /* If score is not initialized, then just return.
1760 * This is likely the case if autorollbacks are not enabled.
1762 if (score == NULL) return NULL;
1764 /* Decrement the reference count */
1767 /* Do we have any more references? If so
1770 if (score->nrefs > 0) return NULL;
1772 rpmlog(RPMLOG_DEBUG, _("\tRefcount is zero...will free\n"));
1773 /* No more references, lets clean up */
1774 /* First deallocate the score entries */
1775 for(i = 0; i < score->entries; i++) {
1776 /* Get the score for the ith entry */
1777 se = score->scores[i];
1779 /* Deallocate the score entries name */
1780 se->N = _free(se->N);
1782 /* Deallocate the score entry itself */
1786 /* Next deallocate the score entry table */
1787 score->scores = _free(score->scores);
1789 /* Finally deallocate the score itself */
1790 score = _free(score);
1796 * XXX: Do not get the score and then store it aside for later use.
1797 * we will delete it out from under you. There is no rpmtsScoreLink()
1798 * as this may be a very temporary fix for autorollbacks.
1800 rpmtsScore rpmtsGetScore(rpmts ts)
1802 if (ts == NULL) return NULL;
1807 * XXX: Do not get the score entry and then store it aside for later use.
1808 * we will delete it out from under you. There is no
1809 * rpmtsScoreEntryLink() as this may be a very temporary fix
1810 * for autorollbacks.
1811 * XXX: The scores are not sorted. This should be fixed at earliest
1812 * opportunity (i.e. when we have the whole autorollback working).
1814 rpmtsScoreEntry rpmtsScoreGetEntry(rpmtsScore score, const char *N)
1818 rpmtsScoreEntry ret = NULL; /* Assume we don't find it */
1820 rpmlog(RPMLOG_DEBUG, _("Looking in score board(%p) for %s\n"), score, N);
1822 /* Try to find the entry in the score list */
1823 for(i = 0; i < score->entries; i++) {
1824 se = score->scores[i];
1825 if (strcmp(N, se->N) == 0) {
1826 rpmlog(RPMLOG_DEBUG, _("\tFound entry at address: %p\n"), se);
1832 /* XXX score->scores undefined. */