2 * \file lib/rpminstall.c
14 /*@access rpmTransactionSet@*/ /* XXX compared with NULL */
15 /*@access Header@*/ /* XXX compared with NULL */
16 /*@access FD_t@*/ /* XXX compared with NULL */
19 * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
20 * @param this memory to free
23 static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
24 if (this) free((void *)this);
28 /* Define if you want percentage progress in the hash bars when
29 * writing to a tty (ordinary hash bars otherwise) --claudio
33 static int hashesPrinted = 0;
36 static int packagesTotal = 0;
37 static int progressTotal = 0;
38 static int progressCurrent = 0;
43 static void printHash(const unsigned long amount, const unsigned long total)
49 if (isatty (STDOUT_FILENO))
53 if (hashesPrinted != hashesTotal) {
54 hashesNeeded = hashesTotal * (total ? (((float) amount) / total) : 1);
55 while (hashesNeeded > hashesPrinted) {
57 if (isatty (STDOUT_FILENO)) {
59 for (i = 0; i < hashesPrinted; i++) putchar ('#');
60 for (; i < hashesTotal; i++) putchar (' ');
62 (int)(100 * (total ? (((float) amount) / total) : 1)));
63 for (i = 0; i < (hashesTotal + 6); i++) putchar ('\b');
71 hashesPrinted = hashesNeeded;
73 if (hashesPrinted == hashesTotal) {
77 for (i = 1; i < hashesPrinted; i++) putchar ('#');
78 printf (" [%3d%%]\n", (int)(100 * (progressTotal ?
79 (((float) progressCurrent) / progressTotal) : 1)));
81 fprintf (stdout, "\n");
90 static void * showProgress(const void * arg, const rpmCallbackType what,
91 const unsigned long amount,
92 const unsigned long total,
93 const void * pkgKey, void * data)
95 Header h = (Header) arg;
97 int flags = (int) ((long)data);
99 const char * filename = pkgKey;
103 case RPMCALLBACK_INST_OPEN_FILE:
104 fd = Fopen(filename, "r.ufdio");
106 fd = fdLink(fd, "persist (showProgress)");
108 /*@notreached@*/ break;
110 case RPMCALLBACK_INST_CLOSE_FILE:
111 fd = fdFree(fd, "persist (showProgress)");
118 case RPMCALLBACK_INST_START:
120 if (!(flags & INSTALL_LABEL))
122 if (flags & INSTALL_HASH) {
123 s = headerSprintf(h, "%{NAME}", rpmTagTable, rpmHeaderFormats,NULL);
125 if (isatty (STDOUT_FILENO))
126 fprintf(stdout, "%4d:%-23.23s", progressCurrent + 1, s);
129 fprintf(stdout, "%-28s", s);
133 s = headerSprintf(h, "%{NAME}-%{VERSION}-%{RELEASE}",
134 rpmTagTable, rpmHeaderFormats, NULL);
135 fprintf(stdout, "%s\n", s);
141 case RPMCALLBACK_TRANS_PROGRESS:
142 case RPMCALLBACK_INST_PROGRESS:
143 if (flags & INSTALL_PERCENT)
144 fprintf(stdout, "%%%% %f\n", (total
145 ? ((float) ((((float) amount) / total) * 100))
147 else if (flags & INSTALL_HASH)
148 printHash(amount, total);
152 case RPMCALLBACK_TRANS_START:
158 if (!(flags & INSTALL_LABEL))
160 if (flags & INSTALL_HASH)
161 fprintf(stdout, "%-28s", _("Preparing..."));
163 printf("%s\n", _("Preparing packages for installation..."));
167 case RPMCALLBACK_TRANS_STOP:
168 if (flags & INSTALL_HASH)
169 printHash(1, 1); /* Fixes "preparing..." progress bar */
171 progressTotal = packagesTotal;
176 case RPMCALLBACK_UNINST_PROGRESS:
177 case RPMCALLBACK_UNINST_START:
178 case RPMCALLBACK_UNINST_STOP:
186 /** @todo Generalize --freshen policies. */
187 int rpmInstall(const char * rootdir, const char ** fileArgv,
188 rpmtransFlags transFlags,
189 rpmInstallInterfaceFlags interfaceFlags,
190 rpmprobFilterFlags probFilter,
191 rpmRelocation * relocations)
197 const char ** pkgURL = NULL;
198 const char ** tmppkgURL = NULL;
199 const char ** fileURL;
201 int numTmpPkgs = 0, numRPMS = 0, numSRPMS = 0;
205 rpmTransactionSet ts = NULL;
208 int notifyFlags = interfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
210 const char ** sourceURL;
211 rpmRelocation * defaultReloc;
213 if (transFlags & RPMTRANS_FLAG_TEST)
216 mode = O_RDWR | O_CREAT; /* XXX can't O_EXCL */
218 for (defaultReloc = relocations; defaultReloc && defaultReloc->oldPath;
220 if (defaultReloc && !defaultReloc->newPath) defaultReloc = NULL;
222 rpmMessage(RPMMESS_DEBUG, _("counting packages to install\n"));
223 for (fileURL = fileArgv, numPkgs = 0; *fileURL; fileURL++, numPkgs++)
226 rpmMessage(RPMMESS_DEBUG, _("found %d packages\n"), numPkgs);
228 pkgURL = xcalloc( (numPkgs + 1), sizeof(*pkgURL) );
229 tmppkgURL = xcalloc( (numPkgs + 1), sizeof(*tmppkgURL) );
231 rpmMessage(RPMMESS_DEBUG, _("looking for packages to download\n"));
232 for (fileURL = fileArgv, i = 0; *fileURL; fileURL++) {
234 switch (urlIsURL(*fileURL)) {
241 const char ** argv = NULL;
243 myrc = rpmGlob(*fileURL, &argc, &argv);
245 rpmMessage(RPMMESS_ERROR,
246 _("skipping %s - rpmGlob failed(%d)\n"),
254 pkgURL = xrealloc(pkgURL, (numPkgs + 1) * sizeof(*pkgURL));
255 tmppkgURL = xrealloc(tmppkgURL, (numPkgs + 1) * sizeof(*tmppkgURL));
258 for (j = 0; j < argc; j++) {
261 fprintf(stdout, _("Retrieving %s\n"), argv[j]);
264 strcpy(tfnbuf, "rpm-xfer.XXXXXX");
265 /*@-unrecog@*/ mktemp(tfnbuf) /*@=unrecog@*/;
266 tfn = rpmGenPath(rootdir, "%{_tmppath}/", tfnbuf);
269 /* XXX undefined %{name}/%{version}/%{release} here */
270 /* XXX %{_tmpdir} does not exist */
271 rpmMessage(RPMMESS_DEBUG, _(" ... as %s\n"), tfn);
272 myrc = urlGetFile(argv[j], tfn);
274 rpmMessage(RPMMESS_ERROR,
275 _("skipping %s - transfer failed - %s\n"),
276 argv[j], ftpStrerror(myrc));
281 tmppkgURL[numTmpPkgs++] = pkgURL[i++] = tfn;
285 for (j = 0; j < argc; j++)
286 argv[j] = _free(argv[j]);
292 pkgURL[i++] = *fileURL;
297 tmppkgURL[numTmpPkgs] = NULL;
299 sourceURL = alloca(sizeof(*sourceURL) * i);
301 rpmMessage(RPMMESS_DEBUG, _("retrieved %d packages\n"), numTmpPkgs);
303 if (numFailed) goto errxit;
306 * Build up the transaction set. As a special case, v1 source packages
307 * are installed right here, only because they don't have headers and
308 * would create all sorts of confusion later.
310 for (fileURL = pkgURL; *fileURL; fileURL++) {
311 const char * fileName;
314 (void) urlPath(*fileURL, &fileName);
315 fd = Fopen(*fileURL, "r.ufdio");
316 if (fd == NULL || Ferror(fd)) {
317 rpmMessage(RPMMESS_ERROR, _("cannot open file %s: %s\n"),
318 *fileURL, Fstrerror(fd));
325 rpmrc = rpmReadPackageHeader(fd, &h, &isSource, &major, NULL);
330 rpmMessage(RPMMESS_ERROR,
331 _("%s does not appear to be a RPM package\n"),
337 case RPMRC_SHORTREAD:
339 rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), *fileURL);
346 sourceURL[numSRPMS++] = fileName;
350 if (rpmdbOpen(rootdir, &db, mode, 0644)) {
352 dn = rpmGetPath( (rootdir ? rootdir : ""),
354 rpmMessage(RPMMESS_ERROR,
355 _("cannot open Packages database in %s\n"), dn);
361 ts = rpmtransCreateSet(db, rootdir);
369 if (headerGetEntry(h, RPMTAG_PREFIXES, NULL,
370 (void **) &paths, &c) && (c == 1)) {
371 defaultReloc->oldPath = xstrdup(paths[0]);
372 paths = _free(paths);
375 headerNVR(h, &name, NULL, NULL);
376 rpmMessage(RPMMESS_ERROR,
377 _("package %s is not relocateable\n"), name);
384 /* On --freshen, verify package is installed and newer */
385 if (interfaceFlags & INSTALL_FRESHEN) {
386 rpmdbMatchIterator mi;
391 headerNVR(h, &name, NULL, NULL);
392 mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
393 count = rpmdbGetIteratorCount(mi);
394 while ((oldH = rpmdbNextIterator(mi)) != NULL) {
395 if (rpmVersionCompare(oldH, h) < 0)
397 /* same or newer package already installed */
401 rpmdbFreeIterator(mi);
405 break; /* XXX out of switch */
407 /* Package is newer than those currently installed. */
410 rc = rpmtransAddPackage(ts, h, NULL, fileName,
411 (interfaceFlags & INSTALL_UPGRADE) != 0,
414 headerFree(h); /* XXX reference held by transaction set */
421 rpmMessage(RPMMESS_ERROR,
422 _("error reading from file %s\n"), *fileURL);
424 /*@notreached@*/ break;
426 rpmMessage(RPMMESS_ERROR,
427 _("file %s requires a newer version of RPM\n"),
430 /*@notreached@*/ break;
433 defaultReloc->oldPath = _free(defaultReloc->oldPath);
441 rpmMessage(RPMMESS_DEBUG, _("found %d source and %d binary packages\n"),
444 if (numFailed) goto errxit;
446 if (numRPMS && !(interfaceFlags & INSTALL_NODEPS)) {
447 struct rpmDependencyConflict * conflicts;
448 if (rpmdepCheck(ts, &conflicts, &numConflicts)) {
453 if (!stopInstall && conflicts) {
454 rpmMessage(RPMMESS_ERROR, _("failed dependencies:\n"));
455 printDepProblems(stderr, conflicts, numConflicts);
456 rpmdepFreeConflicts(conflicts, numConflicts);
462 if (numRPMS && !(interfaceFlags & INSTALL_NOORDER)) {
463 if (rpmdepOrder(ts)) {
469 if (numRPMS && !stopInstall) {
470 rpmProblemSet probs = NULL;
473 packagesTotal = numRPMS;
475 rpmMessage(RPMMESS_DEBUG, _("installing binary packages\n"));
476 rc = rpmRunTransactions(ts, showProgress, (void *) ((long)notifyFlags),
477 NULL, &probs, transFlags, probFilter);
480 numFailed += numRPMS;
483 rpmProblemSetPrint(stderr, probs);
486 if (probs) rpmProblemSetFree(probs);
489 if (numRPMS && ts) rpmtransFree(ts);
491 if (numSRPMS && !stopInstall) {
492 for (i = 0; i < numSRPMS; i++) {
493 fd = Fopen(sourceURL[i], "r.ufdio");
494 if (fd == NULL || Ferror(fd)) {
495 rpmMessage(RPMMESS_ERROR, _("cannot open file %s: %s\n"),
496 sourceURL[i], Fstrerror(fd));
501 if (!(transFlags & RPMTRANS_FLAG_TEST)) {
502 rpmRC rpmrc = rpmInstallSourcePackage(rootdir, fd, NULL,
503 showProgress, (void *) ((long)notifyFlags), NULL);
504 if (rpmrc != RPMRC_OK) numFailed++;
511 for (i = 0; i < numTmpPkgs; i++) {
512 Unlink(tmppkgURL[i]);
513 tmppkgURL[i] = _free(tmppkgURL[i]);
515 tmppkgURL = _free(tmppkgURL);
516 pkgURL = _free(pkgURL);
518 /* FIXME how do we close our various fd's? */
520 if (dbIsOpen) rpmdbClose(db);
525 if (numRPMS && ts) rpmtransFree(ts);
527 for (i = 0; i < numTmpPkgs; i++)
528 tmppkgURL[i] = _free(tmppkgURL[i]);
529 tmppkgURL = _free(tmppkgURL);
531 pkgURL = _free(pkgURL);
532 if (dbIsOpen) rpmdbClose(db);
536 int rpmErase(const char * rootdir, const char ** argv,
537 rpmtransFlags transFlags,
538 rpmEraseInterfaceFlags interfaceFlags)
545 rpmTransactionSet ts;
546 struct rpmDependencyConflict * conflicts;
548 int stopUninstall = 0;
552 if (transFlags & RPMTRANS_FLAG_TEST)
555 mode = O_RDWR | O_EXCL;
557 if (rpmdbOpen(rootdir, &db, mode, 0644)) {
559 dn = rpmGetPath( (rootdir ? rootdir : ""), "%{_dbpath}", NULL);
560 rpmMessage(RPMMESS_ERROR, _("cannot open %s/packages.rpm\n"), dn);
565 ts = rpmtransCreateSet(db, rootdir);
566 for (arg = argv; *arg; arg++) {
567 rpmdbMatchIterator mi;
569 /* XXX HACK to get rpmdbFindByLabel out of the API */
570 mi = rpmdbInitIterator(db, RPMDBI_LABEL, *arg, 0);
571 count = rpmdbGetIteratorCount(mi);
573 rpmMessage(RPMMESS_ERROR, _("package %s is not installed\n"), *arg);
575 } else if (!(count == 1 || (interfaceFlags & UNINSTALL_ALLMATCHES))) {
576 rpmMessage(RPMMESS_ERROR, _("\"%s\" specifies multiple packages\n"),
580 Header h; /* XXX iterator owns the reference */
581 while ((h = rpmdbNextIterator(mi)) != NULL) {
582 unsigned int recOffset = rpmdbGetIteratorOffset(mi);
584 rpmtransRemovePackage(ts, recOffset);
589 rpmdbFreeIterator(mi);
592 if (!(interfaceFlags & UNINSTALL_NODEPS)) {
593 if (rpmdepCheck(ts, &conflicts, &numConflicts)) {
594 numFailed = numPackages;
598 if (!stopUninstall && conflicts) {
599 rpmMessage(RPMMESS_ERROR, _("removing these packages would break "
601 printDepProblems(stderr, conflicts, numConflicts);
602 rpmdepFreeConflicts(conflicts, numConflicts);
603 numFailed += numPackages;
608 if (!stopUninstall) {
609 transFlags |= RPMTRANS_FLAG_REVERSE;
610 numFailed += rpmRunTransactions(ts, NULL, NULL, NULL, &probs,
620 int rpmInstallSource(const char * rootdir, const char * arg,
621 const char ** specFile, char ** cookie)
626 fd = Fopen(arg, "r.ufdio");
627 if (fd == NULL || Ferror(fd)) {
628 rpmMessage(RPMMESS_ERROR, _("cannot open %s: %s\n"), arg, Fstrerror(fd));
634 fprintf(stdout, _("Installing %s\n"), arg);
636 { rpmRC rpmrc = rpmInstallSourcePackage(rootdir, fd, specFile, NULL, NULL,
638 rc = (rpmrc == RPMRC_OK ? 0 : 1);
641 rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), arg);
642 if (specFile && *specFile)
643 *specFile = _free(*specFile);
644 if (cookie && *cookie)
645 *cookie = _free(*cookie);