2 * \file lib/rpminstall.c
7 #include <rpm/rpmcli.h>
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmlib.h> /* rpmReadPackageFile, vercmp etc */
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmts.h>
13 #include <rpm/rpmsq.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmfileutil.h>
17 #include "lib/rpmgi.h"
18 #include "lib/manifest.h"
21 static int rpmcliPackagesTotal = 0;
22 static int rpmcliHashesCurrent = 0;
23 static int rpmcliHashesTotal = 0;
24 static int rpmcliProgressCurrent = 0;
25 static int rpmcliProgressTotal = 0;
26 static int rpmcliProgressState = 0;
29 * Print a CLI progress bar.
30 * @todo Unsnarl isatty(STDOUT_FILENO) from the control flow.
31 * @param amount current
34 static void printHash(const rpm_loff_t amount, const rpm_loff_t total)
38 rpmcliHashesTotal = (isatty (STDOUT_FILENO) ? 34 : 40);
40 if (rpmcliHashesCurrent != rpmcliHashesTotal) {
41 float pct = (total ? (((float) amount) / total) : 1.0);
42 hashesNeeded = (rpmcliHashesTotal * pct) + 0.5;
43 while (hashesNeeded > rpmcliHashesCurrent) {
44 if (isatty (STDOUT_FILENO)) {
46 for (i = 0; i < rpmcliHashesCurrent; i++)
48 for (; i < rpmcliHashesTotal; i++)
50 fprintf(stdout, "(%3d%%)", (int)((100 * pct) + 0.5));
51 for (i = 0; i < (rpmcliHashesTotal + 6); i++)
52 (void) putchar ('\b');
56 rpmcliHashesCurrent++;
58 (void) fflush(stdout);
60 if (rpmcliHashesCurrent == rpmcliHashesTotal) {
62 rpmcliProgressCurrent++;
63 if (isatty(STDOUT_FILENO)) {
64 for (i = 1; i < rpmcliHashesCurrent; i++)
66 pct = (rpmcliProgressTotal
67 ? (((float) rpmcliProgressCurrent) / rpmcliProgressTotal)
69 fprintf(stdout, " [%3d%%]", (int)((100 * pct) + 0.5));
71 fprintf(stdout, "\n");
73 (void) fflush(stdout);
77 static rpmVSFlags setvsFlags(struct rpmInstallArguments_s * ia)
81 if (ia->installInterfaceFlags & (INSTALL_UPGRADE | INSTALL_ERASE))
82 vsflags = rpmExpandNumeric("%{?_vsflags_erase}");
84 vsflags = rpmExpandNumeric("%{?_vsflags_install}");
86 if (rpmcliQueryFlags & VERIFY_DIGEST)
87 vsflags |= _RPMVSF_NODIGESTS;
88 if (rpmcliQueryFlags & VERIFY_SIGNATURE)
89 vsflags |= _RPMVSF_NOSIGNATURES;
90 if (rpmcliQueryFlags & VERIFY_HDRCHK)
91 vsflags |= RPMVSF_NOHDRCHK;
96 void * rpmShowProgress(const void * arg,
97 const rpmCallbackType what,
98 const rpm_loff_t amount,
99 const rpm_loff_t total,
103 Header h = (Header) arg;
104 int flags = (int) ((long)data);
106 const char * filename = (const char *)key;
107 static FD_t fd = NULL;
110 case RPMCALLBACK_INST_OPEN_FILE:
111 if (filename == NULL || filename[0] == '\0')
113 fd = Fopen(filename, "r.ufdio");
114 /* FIX: still necessary? */
115 if (fd == NULL || Ferror(fd)) {
116 rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), filename,
127 case RPMCALLBACK_INST_CLOSE_FILE:
128 /* FIX: still necessary? */
136 case RPMCALLBACK_INST_START:
137 case RPMCALLBACK_UNINST_START:
138 if (rpmcliProgressState != what) {
139 rpmcliProgressState = what;
140 if (flags & INSTALL_HASH) {
141 if (what == RPMCALLBACK_INST_START) {
142 fprintf(stdout, _("Updating / installing...\n"));
144 fprintf(stdout, _("Cleaning up / removing...\n"));
150 rpmcliHashesCurrent = 0;
151 if (h == NULL || !(flags & INSTALL_LABEL))
153 if (flags & INSTALL_HASH) {
154 char *s = headerGetAsString(h, RPMTAG_NEVR);
155 if (isatty (STDOUT_FILENO))
156 fprintf(stdout, "%4d:%-33.33s", rpmcliProgressCurrent + 1, s);
158 fprintf(stdout, "%-38.38s", s);
159 (void) fflush(stdout);
162 char *s = headerGetAsString(h, RPMTAG_NEVRA);
163 fprintf(stdout, "%s\n", s);
164 (void) fflush(stdout);
169 case RPMCALLBACK_INST_STOP:
172 case RPMCALLBACK_TRANS_PROGRESS:
173 case RPMCALLBACK_INST_PROGRESS:
174 case RPMCALLBACK_UNINST_PROGRESS:
175 if (flags & INSTALL_PERCENT)
176 fprintf(stdout, "%%%% %f\n", (double) (total
177 ? ((((float) amount) / total) * 100)
179 else if (flags & INSTALL_HASH)
180 printHash(amount, total);
181 (void) fflush(stdout);
184 case RPMCALLBACK_TRANS_START:
185 rpmcliHashesCurrent = 0;
186 rpmcliProgressTotal = 1;
187 rpmcliProgressCurrent = 0;
188 rpmcliPackagesTotal = total;
189 rpmcliProgressState = what;
190 if (!(flags & INSTALL_LABEL))
192 if (flags & INSTALL_HASH)
193 fprintf(stdout, "%-38s", _("Preparing..."));
195 fprintf(stdout, "%s\n", _("Preparing packages..."));
196 (void) fflush(stdout);
199 case RPMCALLBACK_TRANS_STOP:
200 if (flags & INSTALL_HASH)
201 printHash(1, 1); /* Fixes "preparing..." progress bar */
202 rpmcliProgressTotal = rpmcliPackagesTotal;
203 rpmcliProgressCurrent = 0;
206 case RPMCALLBACK_UNINST_STOP:
208 case RPMCALLBACK_UNPACK_ERROR:
210 case RPMCALLBACK_CPIO_ERROR:
212 case RPMCALLBACK_SCRIPT_ERROR:
214 case RPMCALLBACK_SCRIPT_START:
216 case RPMCALLBACK_SCRIPT_STOP:
218 case RPMCALLBACK_UNKNOWN:
226 static void setNotifyFlag(struct rpmInstallArguments_s * ia,
231 notifyFlags = ia->installInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
232 rpmtsSetNotifyCallback(ts, rpmShowProgress, (void *) ((long)notifyFlags));
248 rpmRelocation * relocations;
252 static int rpmcliTransaction(rpmts ts, struct rpmInstallArguments_s * ia,
260 int eflags = ia->installInterfaceFlags & INSTALL_ERASE;
262 if (!(ia->installInterfaceFlags & INSTALL_NODEPS)) {
264 if (rpmtsCheck(ts)) {
269 ps = rpmtsProblems(ts);
270 if (!stop && rpmpsNumProblems(ps) > 0) {
271 rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
272 rpmpsPrint(NULL, ps);
279 if (!stop && !(ia->installInterfaceFlags & INSTALL_NOORDER)) {
280 if (rpmtsOrder(ts)) {
286 if (numPackages && !stop) {
287 rpmlog(RPMLOG_DEBUG, eflags ? "erasing packages\n" :
288 "installing binary packages\n");
290 rc = rpmtsRun(ts, NULL, ia->probFilter);
292 ps = rpmtsProblems(ts);
294 if (rpmpsNumProblems(ps) > 0 && (eflags || rc > 0))
295 rpmpsPrint(NULL, ps);
302 static int tryReadManifest(struct rpmEIU * eiu)
306 /* Try to read a package manifest. */
307 FD_t fd = Fopen(*eiu->fnp, "r.ufdio");
308 if (fd == NULL || Ferror(fd)) {
309 rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), *eiu->fnp,
320 /* Read list of packages from manifest. */
321 rc = rpmReadPackageManifest(fd, &eiu->argc, &eiu->argv);
323 rpmlog(RPMLOG_ERR, _("%s: not an rpm package (or package manifest): %s\n"),
324 *eiu->fnp, Fstrerror(fd));
328 if (rc != RPMRC_OK) {
336 static int tryReadHeader(rpmts ts, struct rpmEIU * eiu, Header * hdrp)
338 /* Try to read the header from a package file. */
339 FD_t fd = Fopen(*eiu->fnp, "r.ufdio");
340 if (fd == NULL || Ferror(fd)) {
341 rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), *eiu->fnp,
352 /* Read the header, verifying signatures (if present). */
353 eiu->rpmrc = rpmReadPackageFile(ts, fd, *eiu->fnp, hdrp);
357 /* Honor --nomanifest */
358 if (eiu->rpmrc == RPMRC_NOTFOUND && (giFlags & RPMGI_NOMANIFEST))
359 eiu->rpmrc = RPMRC_FAIL;
361 if (eiu->rpmrc == RPMRC_FAIL) {
362 rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), *eiu->fnp);
371 /* On --freshen, verify package is installed and newer */
372 static int checkFreshenStatus(rpmts ts, Header h)
374 rpmdbMatchIterator mi = NULL;
375 const char * name = headerGetString(h, RPMTAG_NAME);
376 const char *arch = headerGetString(h, RPMTAG_ARCH);
380 mi = rpmtsInitIterator(ts, RPMDBI_NAME, name, 0);
381 if (rpmtsColor(ts) && arch)
382 rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch);
384 while ((oldH = rpmdbNextIterator(mi)) != NULL) {
385 /* Package is newer than those currently installed. */
386 if (rpmVersionCompare(oldH, h) < 0)
390 rpmdbFreeIterator(mi);
391 return (oldH != NULL);
394 static int rpmNoGlob(const char *fn, int *argcPtr, ARGV_t * argvPtr)
397 int rc = stat(fn, &sb);
399 argvAdd(argvPtr, fn);
407 /** @todo Generalize --freshen policies. */
408 int rpmInstall(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_t fileArgv)
410 struct rpmEIU * eiu = xcalloc(1, sizeof(*eiu));
411 rpmRelocation * relocations;
412 char * fileURL = NULL;
413 rpmVSFlags vsflags, ovsflags;
417 vsflags = setvsFlags(ia);
418 ovsflags = rpmtsSetVSFlags(ts, (vsflags | RPMVSF_NEEDPAYLOAD));
420 if (fileArgv == NULL) goto exit;
422 (void) rpmtsSetFlags(ts, ia->transFlags);
424 relocations = ia->relocations;
426 setNotifyFlag(ia, ts);
428 if ((eiu->relocations = relocations) != NULL) {
429 while (eiu->relocations->oldPath)
431 if (eiu->relocations->newPath == NULL)
432 eiu->relocations = NULL;
435 /* Build fully globbed list of arguments in argv[argc]. */
436 for (eiu->fnp = fileArgv; *eiu->fnp != NULL; eiu->fnp++) {
440 if (giFlags & RPMGI_NOGLOB) {
441 rc = rpmNoGlob(*eiu->fnp, &ac, &av);
443 char * fn = rpmEscapeSpaces(*eiu->fnp);
444 rc = rpmGlob(fn, &ac, &av);
448 if (giFlags & RPMGI_NOGLOB) {
449 rpmlog(RPMLOG_ERR, _("File not found: %s\n"), *eiu->fnp);
451 rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), *eiu->fnp);
457 argvAppend(&(eiu->argv), av);
463 /* Allocate sufficient storage for next set of args. */
464 if (eiu->pkgx >= eiu->numPkgs) {
465 eiu->numPkgs = eiu->pkgx + eiu->argc;
466 eiu->pkgURL = xrealloc(eiu->pkgURL,
467 (eiu->numPkgs + 1) * sizeof(*eiu->pkgURL));
468 memset(eiu->pkgURL + eiu->pkgx, 0,
469 ((eiu->argc + 1) * sizeof(*eiu->pkgURL)));
470 eiu->pkgState = xrealloc(eiu->pkgState,
471 (eiu->numPkgs + 1) * sizeof(*eiu->pkgState));
472 memset(eiu->pkgState + eiu->pkgx, 0,
473 ((eiu->argc + 1) * sizeof(*eiu->pkgState)));
476 /* Retrieve next set of args, cache on local storage. */
477 for (i = 0; i < eiu->argc; i++) {
478 fileURL = _free(fileURL);
479 fileURL = eiu->argv[i];
482 switch (urlIsURL(fileURL)) {
490 fprintf(stdout, _("Retrieving %s\n"), fileURL);
492 tfd = rpmMkTempFile(rpmtsRootDir(ts), &tfn);
495 rc = urlGetFile(fileURL, tfn);
502 _("skipping %s - transfer failed\n"), fileURL);
504 eiu->pkgURL[eiu->pkgx] = NULL;
508 eiu->pkgState[eiu->pkgx] = 1;
509 eiu->pkgURL[eiu->pkgx] = tfn;
513 case URL_IS_DASH: /* WRONG WRONG WRONG */
514 case URL_IS_HKP: /* WRONG WRONG WRONG */
516 eiu->pkgURL[eiu->pkgx] = fileURL;
522 fileURL = _free(fileURL);
524 if (eiu->numFailed) goto exit;
526 /* Continue processing file arguments, building transaction set. */
527 for (eiu->fnp = eiu->pkgURL+eiu->prevx;
529 eiu->fnp++, eiu->prevx++)
532 const char * fileName;
534 rpmlog(RPMLOG_DEBUG, "============== %s\n", *eiu->fnp);
535 (void) urlPath(*eiu->fnp, &fileName);
537 if (tryReadHeader(ts, eiu, &h) == RPMRC_FAIL)
540 if (eiu->rpmrc == RPMRC_NOTFOUND) {
541 rc = tryReadManifest(eiu);
542 if (rc == RPMRC_OK) {
548 if (headerIsSource(h)) {
549 if (ia->installInterfaceFlags & INSTALL_FRESHEN) {
553 rpmlog(RPMLOG_DEBUG, "\tadded source package [%d]\n",
555 eiu->sourceURL = xrealloc(eiu->sourceURL,
556 (eiu->numSRPMS + 2) * sizeof(*eiu->sourceURL));
557 eiu->sourceURL[eiu->numSRPMS] = *eiu->fnp;
560 eiu->sourceURL[eiu->numSRPMS] = NULL;
564 if (eiu->relocations) {
565 struct rpmtd_s prefixes;
567 headerGet(h, RPMTAG_PREFIXES, &prefixes, HEADERGET_DEFAULT);
568 if (rpmtdCount(&prefixes) == 1) {
569 eiu->relocations->oldPath = xstrdup(rpmtdGetString(&prefixes));
570 rpmtdFreeData(&prefixes);
572 rpmlog(RPMLOG_ERR, _("package %s is not relocatable\n"),
573 headerGetString(h, RPMTAG_NAME));
579 if (ia->installInterfaceFlags & INSTALL_FRESHEN)
580 if (checkFreshenStatus(ts, h) != 1) {
585 if (ia->installInterfaceFlags & INSTALL_REINSTALL)
586 rc = rpmtsAddReinstallElement(ts, h, (fnpyKey)fileName);
588 rc = rpmtsAddInstallElement(ts, h, (fnpyKey)fileName,
589 (ia->installInterfaceFlags & INSTALL_UPGRADE) != 0,
593 if (eiu->relocations)
594 eiu->relocations->oldPath = _free(eiu->relocations->oldPath);
598 rpmlog(RPMLOG_DEBUG, "\tadded binary package [%d]\n",
603 _("error reading from file %s\n"), *eiu->fnp);
616 rpmlog(RPMLOG_DEBUG, "found %d source and %d binary packages\n",
617 eiu->numSRPMS, eiu->numRPMS);
619 if (eiu->numFailed) goto exit;
622 int rc = rpmcliTransaction(ts, ia, eiu->numPkgs);
624 eiu->numFailed += eiu->numRPMS;
626 eiu->numFailed += rc;
629 if (eiu->numSRPMS && (eiu->sourceURL != NULL)) {
630 rpmcliProgressState = 0;
631 rpmcliProgressTotal = 0;
632 rpmcliProgressCurrent = 0;
633 for (i = 0; i < eiu->numSRPMS; i++) {
635 if (eiu->sourceURL[i] != NULL) {
637 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST))
638 rc = rpmInstallSource(ts, eiu->sourceURL[i], NULL, NULL);
646 if (eiu->pkgURL != NULL) {
647 for (i = 0; i < eiu->numPkgs; i++) {
648 if (eiu->pkgURL[i] == NULL) continue;
649 if (eiu->pkgState[i] == 1)
650 (void) unlink(eiu->pkgURL[i]);
651 eiu->pkgURL[i] = _free(eiu->pkgURL[i]);
654 eiu->pkgState = _free(eiu->pkgState);
655 eiu->pkgURL = _free(eiu->pkgURL);
656 eiu->argv = _free(eiu->argv);
661 rpmtsSetVSFlags(ts, ovsflags);
666 int rpmErase(rpmts ts, struct rpmInstallArguments_s * ia, ARGV_const_t argv)
672 rpmVSFlags vsflags, ovsflags;
674 if (argv == NULL) return 0;
676 vsflags = setvsFlags(ia);
677 ovsflags = rpmtsSetVSFlags(ts, vsflags);
679 (void) rpmtsSetFlags(ts, ia->transFlags);
681 setNotifyFlag(ia, ts);
683 qfmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
684 for (arg = argv; *arg; arg++) {
685 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_LABEL, *arg, 0);
686 int matches = rpmdbGetIteratorCount(mi);
690 rpmlog(RPMLOG_ERR, _("package %s is not installed\n"), *arg);
693 Header h; /* XXX iterator owns the reference */
696 !(ia->installInterfaceFlags & UNINSTALL_ALLMATCHES)) {
697 rpmlog(RPMLOG_ERR, _("\"%s\" specifies multiple packages:\n"),
703 while ((h = rpmdbNextIterator(mi)) != NULL) {
705 (void) rpmtsAddEraseElement(ts, h, -1);
708 char *nevra = headerFormat(h, qfmt, NULL);
709 rpmlog(RPMLOG_NOTICE, " %s", nevra);
714 rpmdbFreeIterator(mi);
718 if (numFailed) goto exit;
719 numFailed = rpmcliTransaction(ts, ia, numPackages);
722 rpmtsSetVSFlags(ts, ovsflags);
727 int rpmInstallSource(rpmts ts, const char * arg,
728 char ** specFilePtr, char ** cookie)
734 fd = Fopen(arg, "r.ufdio");
735 if (fd == NULL || Ferror(fd)) {
736 rpmlog(RPMLOG_ERR, _("cannot open %s: %s\n"), arg, Fstrerror(fd));
737 if (fd != NULL) (void) Fclose(fd);
741 if (rpmIsVerbose() && specFilePtr != NULL)
742 fprintf(stdout, _("Installing %s\n"), arg);
745 rpmVSFlags ovsflags =
746 rpmtsSetVSFlags(ts, (specFilePtr) ? (rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD) : rpmtsVSFlags(ts));
747 rpmRC rpmrc = rpmInstallSourcePackage(ts, fd, specFilePtr, cookie);
748 rc = (rpmrc == RPMRC_OK ? 0 : 1);
749 rpmtsSetVSFlags(ts, ovsflags);
752 rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), arg);
753 if (specFilePtr && *specFilePtr)
754 *specFilePtr = _free(*specFilePtr);
755 if (cookie && *cookie)
756 *cookie = _free(*cookie);