Move install langs to per-transaction level
[platform/upstream/rpm.git] / lib / rpmts.c
1 /** \ingroup rpmdep
2  * \file lib/rpmts.c
3  * Routine(s) to handle a "rpmts" transaction sets.
4  */
5 #include "system.h"
6
7 #include <inttypes.h>
8
9 #include <rpm/rpmtypes.h>
10 #include <rpm/rpmlib.h>                 /* rpmReadPackage etc */
11 #include <rpm/rpmurl.h>
12 #include <rpm/rpmmacro.h>
13 #include <rpm/rpmfileutil.h>            /* rpmtsOpenDB() needs rpmGetPath */
14 #include <rpm/rpmstring.h>
15 #include <rpm/rpmkeyring.h>
16
17 #include <rpm/rpmdb.h>
18 #include <rpm/rpmal.h>
19 #include <rpm/rpmds.h>
20 #include <rpm/rpmfi.h>
21 #include <rpm/rpmlog.h>
22 #include <rpm/rpmte.h>
23
24 #include "rpmio/digest.h"
25 #include "lib/rpmlock.h"
26 #include "lib/rpmts_internal.h"
27 #include "lib/misc.h"
28
29 /* XXX FIXME: merge with existing (broken?) tests in system.h */
30 /* portability fiddles */
31 #if STATFS_IN_SYS_STATVFS
32 #include <sys/statvfs.h>
33
34 #else
35 # if STATFS_IN_SYS_VFS
36 #  include <sys/vfs.h>
37 # else
38 #  if STATFS_IN_SYS_MOUNT
39 #   include <sys/mount.h>
40 #  else
41 #   if STATFS_IN_SYS_STATFS
42 #    include <sys/statfs.h>
43 #   endif
44 #  endif
45 # endif
46 #endif
47
48 #include "debug.h"
49
50 static void loadKeyring(rpmts ts);
51
52 int _rpmts_debug = 0;
53
54 int _rpmts_stats = 0;
55
56 rpmts rpmtsUnlink(rpmts ts, const char * msg)
57 {
58 if (_rpmts_debug)
59 fprintf(stderr, "--> ts %p -- %d %s\n", ts, ts->nrefs, msg);
60     ts->nrefs--;
61     return NULL;
62 }
63
64 rpmts rpmtsLink(rpmts ts, const char * msg)
65 {
66     ts->nrefs++;
67 if (_rpmts_debug)
68 fprintf(stderr, "--> ts %p ++ %d %s\n", ts, ts->nrefs, msg);
69     return ts;
70 }
71
72 int rpmtsCloseDB(rpmts ts)
73 {
74     int rc = 0;
75
76     if (ts->rdb != NULL) {
77         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), 
78                         rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
79         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
80                         rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
81         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
82                         rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
83         rc = rpmdbClose(ts->rdb);
84         ts->rdb = NULL;
85     }
86     return rc;
87 }
88
89 int rpmtsOpenDB(rpmts ts, int dbmode)
90 {
91     int rc = 0;
92
93     if (ts->rdb != NULL && ts->dbmode == dbmode)
94         return 0;
95
96     (void) rpmtsCloseDB(ts);
97
98     /* XXX there's a potential db lock race here. */
99
100     ts->dbmode = dbmode;
101     rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
102     if (rc) {
103         char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
104         rpmlog(RPMLOG_ERR,
105                         _("cannot open Packages database in %s\n"), dn);
106         dn = _free(dn);
107     }
108     return rc;
109 }
110
111 int rpmtsInitDB(rpmts ts, int dbmode)
112 {
113     void *lock = rpmtsAcquireLock(ts);
114     int rc = -1;
115     if (lock)
116             rc = rpmdbInit(ts->rootDir, dbmode);
117     rpmtsFreeLock(lock);
118     return rc;
119 }
120
121 int rpmtsGetDBMode(rpmts ts)
122 {
123     assert(ts != NULL);
124     return (ts->dbmode);
125 }
126
127 int rpmtsSetDBMode(rpmts ts, int dbmode)
128 {
129     int rc = 1;
130     /* mode setting only permitted on non-open db */
131     if (ts != NULL && rpmtsGetRdb(ts) == NULL) {
132         ts->dbmode = dbmode;
133         rc = 0;
134     }
135     return rc;
136 }
137
138
139 int rpmtsRebuildDB(rpmts ts)
140 {
141     int rc;
142     void *lock = rpmtsAcquireLock(ts);
143     if (!lock) return -1;
144     if (!(ts->vsflags & RPMVSF_NOHDRCHK))
145         rc = rpmdbRebuild(ts->rootDir, ts, headerCheck);
146     else
147         rc = rpmdbRebuild(ts->rootDir, NULL, NULL);
148     rpmtsFreeLock(lock);
149     return rc;
150 }
151
152 int rpmtsVerifyDB(rpmts ts)
153 {
154     return rpmdbVerify(ts->rootDir);
155 }
156
157 /* keyp might no be defined. */
158 rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmTag rpmtag,
159                         const void * keyp, size_t keylen)
160 {
161     rpmdbMatchIterator mi = NULL;
162     const char * arch = NULL;
163     char *tmp = NULL;
164     int xx;
165
166     if (ts->keyring == NULL)
167         loadKeyring(ts);
168
169     if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
170         return NULL;
171
172     /* Parse out "N(EVR).A" tokens from a label key. */
173     if (rpmtag == RPMDBI_LABEL && keyp != NULL) {
174         const char *se, *s = keyp;
175         char *t;
176         size_t slen = strlen(s);
177         int level = 0;
178         int c;
179
180         tmp = xmalloc(slen+1);
181         keyp = t = tmp;
182         while ((c = *s++) != '\0') {
183             switch (c) {
184             default:
185                 *t++ = c;
186                 break;
187             case '(':
188                 /* XXX Fail if nested parens. */
189                 if (level++ != 0) {
190                     rpmlog(RPMLOG_ERR, _("extra '(' in package label: %s\n"), (const char*)keyp);
191                     goto exit;
192                 }
193                 /* Parse explicit epoch. */
194                 for (se = s; *se && risdigit(*se); se++)
195                     {};
196                 if (*se == ':') {
197                     /* XXX skip explicit epoch's (for now) */
198                     *t++ = '-';
199                     s = se + 1;
200                 } else {
201                     /* No Epoch: found. Convert '(' to '-' and chug. */
202                     *t++ = '-';
203                 }
204                 break;
205             case ')':
206                 /* XXX Fail if nested parens. */
207                 if (--level != 0) {
208                     rpmlog(RPMLOG_ERR, _("missing '(' in package label: %s\n"), (const char*)keyp);
209                     goto exit;
210                 }
211                 /* Don't copy trailing ')' */
212                 break;
213             }
214         }
215         if (level) {
216             rpmlog(RPMLOG_ERR, _("missing ')' in package label: %s\n"), (const char*)keyp);
217             goto exit;
218         }
219         *t = '\0';
220         t = (char *) keyp;
221         t = strrchr(t, '.');
222         /* Is this a valid ".arch" suffix? */
223         if (t != NULL && rpmIsKnownArch(t+1)) {
224            *t++ = '\0';
225            arch = t;
226         }
227     }
228
229     mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
230
231     /* Verify header signature/digest during retrieve (if not disabled). */
232     if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
233         (void) rpmdbSetHdrChk(mi, ts, headerCheck);
234
235     /* Select specified arch only. */
236     if (arch != NULL)
237         xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch);
238
239 exit:
240     free(tmp);
241
242     return mi;
243 }
244
245 rpmKeyring rpmtsGetKeyring(rpmts ts, int autoload)
246 {
247     rpmKeyring keyring = NULL;
248     if (ts) {
249         if (ts->keyring == NULL && autoload) {
250             loadKeyring(ts);
251         }
252         keyring = ts->keyring;
253     }
254     return rpmKeyringLink(keyring);
255 }
256
257 int rpmtsSetKeyring(rpmts ts, rpmKeyring keyring)
258 {
259     /*
260      * Should we permit switching keyring on the fly? For now, require
261      * rpmdb isn't open yet (fairly arbitrary limitation)...
262      */
263     if (ts == NULL || rpmtsGetRdb(ts) != NULL)
264         return -1;
265
266     rpmKeyringFree(ts->keyring);
267     ts->keyring = rpmKeyringLink(keyring);
268     return 0;
269 }
270
271 static int loadKeyringFromFiles(rpmts ts)
272 {
273     ARGV_t files = NULL;
274     /* XXX TODO: deal with chroot path issues */
275     char *pkpath = rpmGetPath(ts->rootDir, "%{_keyringpath}/*.key", NULL);
276     int nkeys = 0;
277
278     rpmlog(RPMLOG_DEBUG, "loading keyring from pubkeys in %s\n", pkpath);
279     if (rpmGlob(pkpath, NULL, &files)) {
280         rpmlog(RPMLOG_DEBUG, "couldn't find any keys in %s\n", pkpath);
281         goto exit;
282     }
283
284     for (char **f = files; *f; f++) {
285         rpmPubkey key = rpmPubkeyRead(*f);
286         if (!key) {
287             rpmlog(RPMLOG_ERR, _("%s: reading of public key failed.\n"), *f);
288             continue;
289         }
290         if (rpmKeyringAddKey(ts->keyring, key) == 0) {
291             nkeys++;
292             rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", *f);
293         }
294         rpmPubkeyFree(key);
295     }
296 exit:
297     free(pkpath);
298     argvFree(files);
299     return nkeys;
300 }
301
302 static int loadKeyringFromDB(rpmts ts)
303 {
304     Header h;
305     rpmdbMatchIterator mi;
306     int nkeys = 0;
307
308     rpmlog(RPMLOG_DEBUG, "loading keyring from rpmdb\n");
309     mi = rpmtsInitIterator(ts, RPMTAG_NAME, "gpg-pubkey", 0);
310     while ((h = rpmdbNextIterator(mi)) != NULL) {
311         struct rpmtd_s pubkeys;
312         const char *key;
313
314         if (!headerGet(h, RPMTAG_PUBKEYS, &pubkeys, HEADERGET_MINMEM))
315            continue;
316
317         while ((key = rpmtdNextString(&pubkeys))) {
318             uint8_t *pkt;
319             size_t pktlen;
320
321             if (b64decode(key, (void **) &pkt, &pktlen) == 0) {
322                 rpmPubkey key = rpmPubkeyNew(pkt, pktlen);
323                 if (rpmKeyringAddKey(ts->keyring, key) == 0) {
324                     char *nvr = headerGetNEVR(h, NULL);
325                     rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", nvr);
326                     free(nvr);
327                     nkeys++;
328                 }
329                 rpmPubkeyFree(key);
330                 free(pkt);
331             }
332         }
333         rpmtdFreeData(&pubkeys);
334     }
335     rpmdbFreeIterator(mi);
336
337     return nkeys;
338 }
339
340 static void loadKeyring(rpmts ts)
341 {
342     ts->keyring = rpmKeyringNew();
343     if (loadKeyringFromFiles(ts) == 0) {
344         if (loadKeyringFromDB(ts) > 0) {
345             /* XXX make this a warning someday... */
346             rpmlog(RPMLOG_DEBUG, "Using legacy gpg-pubkey(s) from rpmdb\n");
347         }
348     }
349 }
350
351 rpmRC rpmtsFindPubkey(rpmts ts, pgpDig dig)
352 {
353     rpmRC res = RPMRC_NOKEY;
354
355     if (dig == NULL)
356         goto exit;
357
358     if (ts->keyring == NULL) {
359         loadKeyring(ts);
360     }
361     res = rpmKeyringLookup(ts->keyring, dig);
362
363 exit:
364     return res;
365 }
366
367 /* Build pubkey header. */
368 static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header h)
369 {
370     const char * afmt = "%{pubkeys:armor}";
371     const char * group = "Public Keys";
372     const char * license = "pubkey";
373     const char * buildhost = "localhost";
374     rpmsenseFlags pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
375     uint32_t zero = 0;
376     pgpDig dig = NULL;
377     pgpDigParams pubp = NULL;
378     char * d = NULL;
379     char * enc = NULL;
380     char * n = NULL;
381     char * u = NULL;
382     char * v = NULL;
383     char * r = NULL;
384     char * evr = NULL;
385     int rc = -1;
386
387     if ((enc = rpmPubkeyBase64(key)) == NULL)
388         goto exit;
389     if ((dig = rpmPubkeyDig(key)) == NULL)
390         goto exit;
391
392     /* Build header elements. */
393     pubp = &dig->pubkey;
394     v = pgpHexStr(pubp->signid, sizeof(pubp->signid)); 
395     r = pgpHexStr(pubp->time, sizeof(pubp->time));
396
397     rasprintf(&n, "gpg(%s)", v+8);
398     rasprintf(&u, "gpg(%s)", pubp->userid ? pubp->userid : "none");
399     rasprintf(&evr, "%d:%s-%s", pubp->version, v, r);
400
401     headerPutString(h, RPMTAG_PUBKEYS, enc);
402
403     if ((d = headerFormat(h, afmt, NULL)) == NULL)
404         goto exit;
405
406     headerPutString(h, RPMTAG_NAME, "gpg-pubkey");
407     headerPutString(h, RPMTAG_VERSION, v+8);
408     headerPutString(h, RPMTAG_RELEASE, r);
409     headerPutString(h, RPMTAG_DESCRIPTION, d);
410     headerPutString(h, RPMTAG_GROUP, group);
411     headerPutString(h, RPMTAG_LICENSE, license);
412     headerPutString(h, RPMTAG_SUMMARY, u);
413
414     headerPutUint32(h, RPMTAG_SIZE, &zero, 1);
415
416     headerPutString(h, RPMTAG_PROVIDENAME, u);
417     headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
418     headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
419         
420     headerPutString(h, RPMTAG_PROVIDENAME, n);
421     headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
422     headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
423
424     headerPutString(h, RPMTAG_RPMVERSION, RPMVERSION);
425     headerPutString(h, RPMTAG_BUILDHOST, buildhost);
426
427     {   rpm_tid_t tid = rpmtsGetTid(ts);
428         headerPutUint32(h, RPMTAG_INSTALLTIME, &tid, 1);
429         headerPutUint32(h, RPMTAG_BUILDTIME, &tid, 1);
430     }
431     rc = 0;
432
433 exit:
434     pgpFreeDig(dig);
435     free(n);
436     free(u);
437     free(v);
438     free(r);
439     free(evr);
440     free(enc);
441     free(d);
442
443     return rc;
444 }
445
446 rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen)
447 {
448     Header h = headerNew();
449     rpmRC rc = RPMRC_FAIL;              /* assume failure */
450     rpmPubkey pubkey = NULL;
451     rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
452
453     if ((pubkey = rpmPubkeyNew(pkt, pktlen)) == NULL)
454         goto exit;
455     if (rpmKeyringAddKey(keyring, pubkey) != 0)
456         goto exit;
457     if (makePubkeyHeader(ts, pubkey, h) != 0) 
458         goto exit;
459
460     /* Add header to database. */
461     if (rpmtsOpenDB(ts, (O_RDWR|O_CREAT)))
462         goto exit;
463     if (rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h, NULL, NULL) != 0)
464         goto exit;
465     rc = RPMRC_OK;
466
467 exit:
468     /* Clean up. */
469     headerFree(h);
470     rpmPubkeyFree(pubkey);
471     rpmKeyringFree(keyring);
472     return rc;
473 }
474
475 int rpmtsSetSolveCallback(rpmts ts,
476                 int (*solve) (rpmts ts, rpmds key, const void * data),
477                 const void * solveData)
478 {
479     int rc = 0;
480
481     if (ts) {
482         ts->solve = solve;
483         ts->solveData = solveData;
484     }
485     return rc;
486 }
487
488 rpmps rpmtsProblems(rpmts ts)
489 {
490     rpmps ps = NULL;
491     if (ts) {
492         if (ts->probs)
493             ps = rpmpsLink(ts->probs, RPMDBG_M("rpmtsProblems"));
494     }
495     return ps;
496 }
497
498 void rpmtsCleanProblems(rpmts ts)
499 {
500     if (ts && ts->probs) {
501         ts->probs = rpmpsFree(ts->probs);
502     }
503 }
504
505 void rpmtsClean(rpmts ts)
506 {
507     rpmtsi pi; rpmte p;
508
509     if (ts == NULL)
510         return;
511
512     /* Clean up after dependency checks. */
513     pi = rpmtsiInit(ts);
514     while ((p = rpmtsiNext(pi, 0)) != NULL)
515         rpmteCleanDS(p);
516     pi = rpmtsiFree(pi);
517
518     ts->addedPackages = rpmalFree(ts->addedPackages);
519     ts->numAddedPackages = 0;
520
521     rpmtsCleanProblems(ts);
522 }
523
524 void rpmtsEmpty(rpmts ts)
525 {
526     if (ts == NULL)
527         return;
528
529     rpmtsClean(ts);
530
531     for (int oc = 0; oc < ts->orderCount; oc++) {
532         ts->order[oc] = rpmteFree(ts->order[oc]);
533     }
534
535     ts->orderCount = 0;
536     ts->ntrees = 0;
537     ts->maxDepth = 0;
538
539     ts->numRemovedPackages = 0;
540     return;
541 }
542
543 static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
544 {
545     static const unsigned int scale = (1000 * 1000);
546     if (op != NULL && op->count > 0)
547         fprintf(stderr, "   %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
548                 name, op->count,
549                 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
550                 op->usecs/scale, op->usecs%scale);
551 }
552
553 static void rpmtsPrintStats(rpmts ts)
554 {
555     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
556
557     rpmtsPrintStat("total:       ", rpmtsOp(ts, RPMTS_OP_TOTAL));
558     rpmtsPrintStat("check:       ", rpmtsOp(ts, RPMTS_OP_CHECK));
559     rpmtsPrintStat("order:       ", rpmtsOp(ts, RPMTS_OP_ORDER));
560     rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
561     rpmtsPrintStat("install:     ", rpmtsOp(ts, RPMTS_OP_INSTALL));
562     rpmtsPrintStat("erase:       ", rpmtsOp(ts, RPMTS_OP_ERASE));
563     rpmtsPrintStat("scriptlets:  ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
564     rpmtsPrintStat("compress:    ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
565     rpmtsPrintStat("uncompress:  ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
566     rpmtsPrintStat("digest:      ", rpmtsOp(ts, RPMTS_OP_DIGEST));
567     rpmtsPrintStat("signature:   ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
568     rpmtsPrintStat("dbadd:       ", rpmtsOp(ts, RPMTS_OP_DBADD));
569     rpmtsPrintStat("dbremove:    ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
570     rpmtsPrintStat("dbget:       ", rpmtsOp(ts, RPMTS_OP_DBGET));
571     rpmtsPrintStat("dbput:       ", rpmtsOp(ts, RPMTS_OP_DBPUT));
572     rpmtsPrintStat("dbdel:       ", rpmtsOp(ts, RPMTS_OP_DBDEL));
573 }
574
575 rpmts rpmtsFree(rpmts ts)
576 {
577     if (ts == NULL)
578         return NULL;
579
580     if (ts->nrefs > 1)
581         return rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
582
583     rpmtsEmpty(ts);
584
585     (void) rpmtsCloseDB(ts);
586
587     ts->removedPackages = _free(ts->removedPackages);
588
589     ts->dsi = _free(ts->dsi);
590
591     if (ts->scriptFd != NULL) {
592         ts->scriptFd = fdFree(ts->scriptFd, RPMDBG_M("rpmtsFree"));
593         ts->scriptFd = NULL;
594     }
595     ts->rootDir = _free(ts->rootDir);
596     ts->currDir = _free(ts->currDir);
597
598     ts->order = _free(ts->order);
599     ts->orderAlloced = 0;
600
601     ts->keyring = rpmKeyringFree(ts->keyring);
602     ts->netsharedPaths = argvFree(ts->netsharedPaths);
603     ts->installLangs = argvFree(ts->installLangs);
604
605     if (_rpmts_stats)
606         rpmtsPrintStats(ts);
607
608     (void) rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
609
610     ts = _free(ts);
611
612     return NULL;
613 }
614
615 rpmVSFlags rpmtsVSFlags(rpmts ts)
616 {
617     rpmVSFlags vsflags = 0;
618     if (ts != NULL)
619         vsflags = ts->vsflags;
620     return vsflags;
621 }
622
623 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
624 {
625     rpmVSFlags ovsflags = 0;
626     if (ts != NULL) {
627         ovsflags = ts->vsflags;
628         ts->vsflags = vsflags;
629     }
630     return ovsflags;
631 }
632
633 int rpmtsUnorderedSuccessors(rpmts ts, int first)
634 {
635     int unorderedSuccessors = 0;
636     if (ts != NULL) {
637         unorderedSuccessors = ts->unorderedSuccessors;
638         if (first >= 0)
639             ts->unorderedSuccessors = first;
640     }
641     return unorderedSuccessors;
642 }
643
644 const char * rpmtsRootDir(rpmts ts)
645 {
646     const char * rootDir = NULL;
647
648     if (ts != NULL && ts->rootDir != NULL) {
649         urltype ut = urlPath(ts->rootDir, &rootDir);
650         switch (ut) {
651         case URL_IS_UNKNOWN:
652         case URL_IS_PATH:
653             break;
654         /* XXX these shouldn't be allowed as rootdir! */
655         case URL_IS_HTTPS:
656         case URL_IS_HTTP:
657         case URL_IS_HKP:
658         case URL_IS_FTP:
659         case URL_IS_DASH:
660         default:
661             rootDir = "/";
662             break;
663         }
664     }
665     return rootDir;
666 }
667
668 int rpmtsSetRootDir(rpmts ts, const char * rootDir)
669 {
670     if (ts == NULL || (rootDir && rootDir[0] != '/')) {
671         return -1;
672     }
673
674     ts->rootDir = _free(ts->rootDir);
675     /* Ensure clean path with a trailing slash */
676     ts->rootDir = rootDir ? rpmGetPath(rootDir, NULL) : xstrdup("/");
677     if (strcmp(ts->rootDir, "/") != 0) {
678         rstrcat(&ts->rootDir, "/");
679     }
680     return 0;
681 }
682
683 const char * rpmtsCurrDir(rpmts ts)
684 {
685     const char * currDir = NULL;
686     if (ts != NULL) {
687         currDir = ts->currDir;
688     }
689     return currDir;
690 }
691
692 void rpmtsSetCurrDir(rpmts ts, const char * currDir)
693 {
694     if (ts != NULL) {
695         ts->currDir = _free(ts->currDir);
696         if (currDir)
697             ts->currDir = xstrdup(currDir);
698     }
699 }
700
701 FD_t rpmtsScriptFd(rpmts ts)
702 {
703     FD_t scriptFd = NULL;
704     if (ts != NULL) {
705         scriptFd = ts->scriptFd;
706     }
707     return scriptFd;
708 }
709
710 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
711 {
712
713     if (ts != NULL) {
714         if (ts->scriptFd != NULL) {
715             ts->scriptFd = fdFree(ts->scriptFd, 
716                                   RPMDBG_M("rpmtsSetScriptFd"));
717             ts->scriptFd = NULL;
718         }
719         if (scriptFd != NULL)
720             ts->scriptFd = fdLink((void *)scriptFd, 
721                                   RPMDBG_M("rpmtsSetScriptFd"));
722     }
723 }
724
725 int rpmtsSELinuxEnabled(rpmts ts)
726 {
727     return (ts != NULL ? (ts->selinuxEnabled > 0) : 0);
728 }
729
730 int rpmtsChrootDone(rpmts ts)
731 {
732     return (ts != NULL ? ts->chrootDone : 0);
733 }
734
735 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
736 {
737     int ochrootDone = 0;
738     if (ts != NULL) {
739         ochrootDone = ts->chrootDone;
740         rpmdbSetChrootDone(rpmtsGetRdb(ts), chrootDone);
741         ts->chrootDone = chrootDone;
742     }
743     return ochrootDone;
744 }
745
746 rpm_tid_t rpmtsGetTid(rpmts ts)
747 {
748     rpm_tid_t tid = -1;  /* XXX -1 is time(2) error return. */
749     if (ts != NULL) {
750         tid = ts->tid;
751     }
752     return tid;
753 }
754
755 rpm_tid_t rpmtsSetTid(rpmts ts, rpm_tid_t tid)
756 {
757     rpm_tid_t otid = -1; /* XXX -1 is time(2) error return. */
758     if (ts != NULL) {
759         otid = ts->tid;
760         ts->tid = tid;
761     }
762     return otid;
763 }
764
765 rpmdb rpmtsGetRdb(rpmts ts)
766 {
767     rpmdb rdb = NULL;
768     if (ts != NULL) {
769         rdb = ts->rdb;
770     }
771     return rdb;
772 }
773
774 int rpmtsInitDSI(const rpmts ts)
775 {
776     rpmDiskSpaceInfo dsi;
777     struct stat sb;
778     int rc;
779     int i;
780
781     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
782         return 0;
783
784     rpmlog(RPMLOG_DEBUG, "mounted filesystems:\n");
785     rpmlog(RPMLOG_DEBUG,
786         "    i        dev    bsize       bavail       iavail mount point\n");
787
788     rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
789     if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
790         return rc;
791
792     /* Get available space on mounted file systems. */
793
794     ts->dsi = _free(ts->dsi);
795     ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
796
797     dsi = ts->dsi;
798
799     if (dsi != NULL)
800     for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
801 #if STATFS_IN_SYS_STATVFS
802         struct statvfs sfb;
803         memset(&sfb, 0, sizeof(sfb));
804         rc = statvfs(ts->filesystems[i], &sfb);
805 #else
806         struct statfs sfb;
807         memset(&sfb, 0, sizeof(sfb));
808 #  if STAT_STATFS4
809 /* This platform has the 4-argument version of the statfs call.  The last two
810  * should be the size of struct statfs and 0, respectively.  The 0 is the
811  * filesystem type, and is always 0 when statfs is called on a mounted
812  * filesystem, as we're doing.
813  */
814         rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
815 #  else
816         rc = statfs(ts->filesystems[i], &sfb);
817 #  endif
818 #endif
819         if (rc)
820             break;
821
822         rc = stat(ts->filesystems[i], &sb);
823         if (rc)
824             break;
825         dsi->dev = sb.st_dev;
826
827         dsi->bsize = sfb.f_bsize;
828         dsi->bneeded = 0;
829         dsi->ineeded = 0;
830 #ifdef STATFS_HAS_F_BAVAIL
831         dsi->bavail = sfb.f_bavail;
832 #else
833 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
834  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
835  * it's about all we can do.
836  */
837         dsi->bavail = sfb.f_blocks - sfb.f_bfree;
838 #endif
839         /* XXX Avoid FAT and other file systems that have not inodes. */
840         /* XXX assigning negative value to unsigned type */
841         dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
842                                 ? sfb.f_ffree : -1;
843         rpmlog(RPMLOG_DEBUG, 
844                 "%5d 0x%08x %8" PRId64 " %12" PRId64 " %12" PRId64" %s\n",
845                 i, (unsigned) dsi->dev, dsi->bsize,
846                 dsi->bavail, dsi->iavail,
847                 ts->filesystems[i]);
848     }
849     return rc;
850 }
851
852 void rpmtsUpdateDSI(const rpmts ts, dev_t dev,
853                 rpm_loff_t fileSize, rpm_loff_t prevSize, rpm_loff_t fixupSize,
854                 rpmFileAction action)
855 {
856     rpmDiskSpaceInfo dsi;
857     int64_t bneeded;
858
859     dsi = ts->dsi;
860     if (dsi) {
861         while (dsi->bsize && dsi->dev != dev)
862             dsi++;
863         if (dsi->bsize == 0)
864             dsi = NULL;
865     }
866     if (dsi == NULL)
867         return;
868
869     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
870
871     switch (action) {
872     case FA_BACKUP:
873     case FA_SAVE:
874     case FA_ALTNAME:
875         dsi->ineeded++;
876         dsi->bneeded += bneeded;
877         break;
878
879     /*
880      * FIXME: If two packages share a file (same md5sum), and
881      * that file is being replaced on disk, will dsi->bneeded get
882      * adjusted twice? Quite probably!
883      */
884     case FA_CREATE:
885         dsi->bneeded += bneeded;
886         dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
887         break;
888
889     case FA_ERASE:
890         dsi->ineeded--;
891         dsi->bneeded -= bneeded;
892         break;
893
894     default:
895         break;
896     }
897
898     if (fixupSize)
899         dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
900 }
901
902 void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
903 {
904     rpmDiskSpaceInfo dsi;
905     rpmps ps;
906     int fc;
907     int i;
908
909     if (ts->filesystems == NULL || ts->filesystemCount <= 0)
910         return;
911
912     dsi = ts->dsi;
913     if (dsi == NULL)
914         return;
915     fc = rpmfiFC(rpmteFI(te));
916     if (fc <= 0)
917         return;
918
919     ps = rpmtsProblems(ts);
920     for (i = 0; i < ts->filesystemCount; i++, dsi++) {
921
922         if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
923             rpmpsAppend(ps, RPMPROB_DISKSPACE,
924                         rpmteNEVRA(te), rpmteKey(te),
925                         ts->filesystems[i], NULL, NULL,
926            (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
927         }
928
929         if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
930             rpmpsAppend(ps, RPMPROB_DISKNODES,
931                         rpmteNEVRA(te), rpmteKey(te),
932                         ts->filesystems[i], NULL, NULL,
933             (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
934         }
935     }
936     ps = rpmpsFree(ps);
937 }
938
939 void * rpmtsNotify(rpmts ts, rpmte te,
940                 rpmCallbackType what, rpm_loff_t amount, rpm_loff_t total)
941 {
942     void * ptr = NULL;
943     if (ts && ts->notify) {
944         Header h = NULL;
945         fnpyKey cbkey = NULL;
946         if (te) {
947             h = rpmteHeader(te);
948             cbkey = rpmteKey(te);
949         }
950         ptr = ts->notify(h, what, amount, total, cbkey, ts->notifyData);
951
952         if (h) {
953             headerUnlink(h); /* undo rpmteHeader() ref */
954         }
955     }
956     return ptr;
957 }
958
959 int rpmtsNElements(rpmts ts)
960 {
961     int nelements = 0;
962     if (ts != NULL && ts->order != NULL) {
963         nelements = ts->orderCount;
964     }
965     return nelements;
966 }
967
968 rpmte rpmtsElement(rpmts ts, int ix)
969 {
970     rpmte te = NULL;
971     if (ts != NULL && ts->order != NULL) {
972         if (ix >= 0 && ix < ts->orderCount)
973             te = ts->order[ix];
974     }
975     return te;
976 }
977
978 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
979 {
980     return (ts != NULL ? ts->ignoreSet : 0);
981 }
982
983 rpmtransFlags rpmtsFlags(rpmts ts)
984 {
985     return (ts != NULL ? ts->transFlags : 0);
986 }
987
988 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
989 {
990     rpmtransFlags otransFlags = 0;
991     if (ts != NULL) {
992         otransFlags = ts->transFlags;
993         ts->transFlags = transFlags;
994     }
995     return otransFlags;
996 }
997
998 rpmSpec rpmtsSpec(rpmts ts)
999 {
1000     return ts->spec;
1001 }
1002
1003 rpmSpec rpmtsSetSpec(rpmts ts, rpmSpec spec)
1004 {
1005     rpmSpec ospec = ts->spec;
1006     ts->spec = spec;
1007     return ospec;
1008 }
1009
1010 rpmte rpmtsRelocateElement(rpmts ts)
1011 {
1012     return ts->relocateElement;
1013 }
1014
1015 rpmte rpmtsSetRelocateElement(rpmts ts, rpmte relocateElement)
1016 {
1017     rpmte orelocateElement = ts->relocateElement;
1018     ts->relocateElement = relocateElement;
1019     return orelocateElement;
1020 }
1021
1022 rpm_color_t rpmtsColor(rpmts ts)
1023 {
1024     return (ts != NULL ? ts->color : 0);
1025 }
1026
1027 rpm_color_t rpmtsSetColor(rpmts ts, rpm_color_t color)
1028 {
1029     rpm_color_t ocolor = 0;
1030     if (ts != NULL) {
1031         ocolor = ts->color;
1032         ts->color = color;
1033     }
1034     return ocolor;
1035 }
1036
1037 rpm_color_t rpmtsPrefColor(rpmts ts)
1038 {
1039     return (ts != NULL ? ts->prefcolor : 0);
1040 }
1041
1042 rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
1043 {
1044     rpmop op = NULL;
1045
1046     if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
1047         op = ts->ops + opx;
1048     return op;
1049 }
1050
1051 int rpmtsSetNotifyCallback(rpmts ts,
1052                 rpmCallbackFunction notify, rpmCallbackData notifyData)
1053 {
1054     if (ts != NULL) {
1055         ts->notify = notify;
1056         ts->notifyData = notifyData;
1057     }
1058     return 0;
1059 }
1060
1061 int rpmtsGetKeys(const rpmts ts, fnpyKey ** ep, int * nep)
1062 {
1063     int rc = 0;
1064
1065     if (nep) *nep = ts->orderCount;
1066     if (ep) {
1067         rpmtsi pi;      rpmte p;
1068         fnpyKey * e;
1069
1070         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
1071         pi = rpmtsiInit(ts);
1072         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1073             switch (rpmteType(p)) {
1074             case TR_ADDED:
1075                 *e = rpmteKey(p);
1076                 break;
1077             case TR_REMOVED:
1078             default:
1079                 *e = NULL;
1080                 break;
1081             }
1082             e++;
1083         }
1084         pi = rpmtsiFree(pi);
1085     }
1086     return rc;
1087 }
1088
1089 rpmts rpmtsCreate(void)
1090 {
1091     rpmts ts;
1092
1093     ts = xcalloc(1, sizeof(*ts));
1094     memset(&ts->ops, 0, sizeof(ts->ops));
1095     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
1096     ts->filesystemCount = 0;
1097     ts->filesystems = NULL;
1098     ts->dsi = NULL;
1099
1100     ts->solve = NULL;
1101     ts->solveData = NULL;
1102
1103     ts->rdb = NULL;
1104     ts->dbmode = O_RDONLY;
1105
1106     ts->scriptFd = NULL;
1107     ts->tid = (rpm_tid_t) time(NULL);
1108     ts->delta = 5;
1109
1110     ts->color = rpmExpandNumeric("%{?_transaction_color}");
1111     ts->prefcolor = rpmExpandNumeric("%{?_prefer_color}")?:2;
1112
1113     ts->netsharedPaths = NULL;
1114     ts->installLangs = NULL;
1115     {   char *tmp = rpmExpand("%{_netsharedpath}", NULL);
1116         if (tmp && *tmp != '%') {
1117             argvSplit(&ts->netsharedPaths, tmp, ":");
1118         }
1119         free(tmp);
1120
1121         tmp = rpmExpand("%{_install_langs}", NULL);
1122         if (tmp && *tmp != '%') {
1123             ARGV_t langs = NULL;
1124             argvSplit(&langs, tmp, ":");        
1125             /* If we'll be installing all languages anyway, don't bother */
1126             for (ARGV_t l = langs; *l; l++) {
1127                 if (strcmp(*l, "all") == 0) {
1128                     langs = argvFree(langs);
1129                     break;
1130                 }
1131             }
1132             ts->installLangs = langs;
1133         }
1134         free(tmp);
1135     }
1136
1137     ts->numRemovedPackages = 0;
1138     ts->allocedRemovedPackages = ts->delta;
1139     ts->removedPackages = xcalloc(ts->allocedRemovedPackages,
1140                         sizeof(*ts->removedPackages));
1141
1142     ts->rootDir = NULL;
1143     ts->currDir = NULL;
1144     ts->chrootDone = 0;
1145
1146     ts->selinuxEnabled = is_selinux_enabled();
1147
1148     ts->numAddedPackages = 0;
1149     ts->addedPackages = NULL;
1150
1151     ts->orderAlloced = 0;
1152     ts->orderCount = 0;
1153     ts->order = NULL;
1154     ts->ntrees = 0;
1155     ts->maxDepth = 0;
1156
1157     ts->probs = NULL;
1158
1159     ts->keyring = NULL;
1160
1161     ts->nrefs = 0;
1162
1163     return rpmtsLink(ts, RPMDBG_M("tsCreate"));
1164 }
1165