7d881b0e52b70959776d04c37674084969889b76
[tools/librpm-tizen.git] / lib / rpminstall.c
1 /** \ingroup rpmcli
2  * \file lib/rpminstall.c
3  */
4
5 #include "system.h"
6
7 #include <rpmlib.h>
8 #include <rpmmacro.h>
9 #include <rpmurl.h>
10
11 #include "misc.h"
12 #include "debug.h"
13
14 /*@access rpmTransactionSet@*/  /* XXX compared with NULL */
15 /*@access Header@*/             /* XXX compared with NULL */
16 /*@access FD_t@*/               /* XXX compared with NULL */
17
18 /**
19  * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
20  * @param this          memory to free
21  * @retval              NULL always
22  */
23 static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
24     if (this)   free((void *)this);
25     return NULL;
26 }
27
28 /* Define if you want percentage progress in the hash bars when
29  * writing to a tty (ordinary hash bars otherwise) --claudio
30  */
31 #define FANCY_HASH
32
33 static int hashesPrinted = 0;
34
35 #ifdef FANCY_HASH
36 static int packagesTotal = 0;
37 static int progressTotal = 0;
38 static int progressCurrent = 0;
39 #endif
40
41 /**
42  */
43 static void printHash(const unsigned long amount, const unsigned long total)
44 {
45     int hashesNeeded;
46     int hashesTotal = 50;
47
48 #ifdef FANCY_HASH
49     if (isatty (STDOUT_FILENO))
50         hashesTotal = 44;
51 #endif
52
53     if (hashesPrinted != hashesTotal) {
54         hashesNeeded = hashesTotal * (total ? (((float) amount) / total) : 1);
55         while (hashesNeeded > hashesPrinted) {
56 #ifdef FANCY_HASH
57             if (isatty (STDOUT_FILENO)) {
58                 int i;
59                 for (i = 0; i < hashesPrinted; i++) putchar ('#');
60                 for (; i < hashesTotal; i++) putchar (' ');
61                 printf ("(%3d%%)",
62                         (int)(100 * (total ? (((float) amount) / total) : 1)));
63                 for (i = 0; i < (hashesTotal + 6); i++) putchar ('\b');
64             } else
65 #endif
66             fprintf(stdout, "#");
67
68             hashesPrinted++;
69         }
70         fflush(stdout);
71         hashesPrinted = hashesNeeded;
72
73         if (hashesPrinted == hashesTotal) {
74 #ifdef FANCY_HASH
75             int i;
76             progressCurrent++;
77             for (i = 1; i < hashesPrinted; i++) putchar ('#');
78             printf (" [%3d%%]\n", (int)(100 * (progressTotal ?
79                         (((float) progressCurrent) / progressTotal) : 1)));
80 #else
81             fprintf (stdout, "\n");
82 #endif
83         }
84         fflush(stdout);
85     }
86 }
87
88 /**
89  */
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)
94 {
95     Header h = (Header) arg;
96     char * s;
97     int flags = (int) ((long)data);
98     void * rc = NULL;
99     const char * filename = pkgKey;
100     static FD_t fd;
101
102     switch (what) {
103     case RPMCALLBACK_INST_OPEN_FILE:
104         fd = Fopen(filename, "r.ufdio");
105         if (fd)
106             fd = fdLink(fd, "persist (showProgress)");
107         return fd;
108         /*@notreached@*/ break;
109
110     case RPMCALLBACK_INST_CLOSE_FILE:
111         fd = fdFree(fd, "persist (showProgress)");
112         if (fd) {
113             Fclose(fd);
114             fd = NULL;
115         }
116         break;
117
118     case RPMCALLBACK_INST_START:
119         hashesPrinted = 0;
120         if (!(flags & INSTALL_LABEL))
121             break;
122         if (flags & INSTALL_HASH) {
123             s = headerSprintf(h, "%{NAME}", rpmTagTable, rpmHeaderFormats,NULL);
124 #ifdef FANCY_HASH
125             if (isatty (STDOUT_FILENO))
126                 fprintf(stdout, "%4d:%-23.23s", progressCurrent + 1, s);
127             else
128 #else
129                 fprintf(stdout, "%-28s", s);
130 #endif
131             fflush(stdout);
132         } else {
133             s = headerSprintf(h, "%{NAME}-%{VERSION}-%{RELEASE}", 
134                                   rpmTagTable, rpmHeaderFormats, NULL);
135             fprintf(stdout, "%s\n", s);
136             fflush(stdout);
137         }
138         s = _free(s);
139         break;
140
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))
146                                 : 100.0));
147         else if (flags & INSTALL_HASH)
148             printHash(amount, total);
149         fflush(stdout);
150         break;
151
152     case RPMCALLBACK_TRANS_START:
153         hashesPrinted = 0;
154 #ifdef FANCY_HASH
155         progressTotal = 1;
156         progressCurrent = 0;
157 #endif
158         if (!(flags & INSTALL_LABEL))
159             break;
160         if (flags & INSTALL_HASH)
161             fprintf(stdout, "%-28s", _("Preparing..."));
162         else
163             printf("%s\n", _("Preparing packages for installation..."));
164         fflush(stdout);
165         break;
166
167     case RPMCALLBACK_TRANS_STOP:
168         if (flags & INSTALL_HASH)
169             printHash(1, 1);    /* Fixes "preparing..." progress bar */
170 #ifdef FANCY_HASH
171         progressTotal = packagesTotal;
172         progressCurrent = 0;
173 #endif
174         break;
175
176     case RPMCALLBACK_UNINST_PROGRESS:
177     case RPMCALLBACK_UNINST_START:
178     case RPMCALLBACK_UNINST_STOP:
179         /* ignore */
180         break;
181     }
182
183     return rc;
184 }       
185
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)
192 {
193     rpmdb db = NULL;
194     FD_t fd;
195     int i;
196     int mode, rc, major;
197     const char ** pkgURL = NULL;
198     const char ** tmppkgURL = NULL;
199     const char ** fileURL;
200     int numPkgs;
201     int numTmpPkgs = 0, numRPMS = 0, numSRPMS = 0;
202     int numFailed = 0;
203     Header h;
204     int isSource;
205     rpmTransactionSet ts = NULL;
206     int numConflicts;
207     int stopInstall = 0;
208     int notifyFlags = interfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
209     int dbIsOpen = 0;
210     const char ** sourceURL;
211     rpmRelocation * defaultReloc;
212
213     if (transFlags & RPMTRANS_FLAG_TEST) 
214         mode = O_RDONLY;
215     else
216         mode = O_RDWR | O_CREAT;        /* XXX can't O_EXCL */
217
218     for (defaultReloc = relocations; defaultReloc && defaultReloc->oldPath;
219          defaultReloc++);
220     if (defaultReloc && !defaultReloc->newPath) defaultReloc = NULL;
221
222     rpmMessage(RPMMESS_DEBUG, _("counting packages to install\n"));
223     for (fileURL = fileArgv, numPkgs = 0; *fileURL; fileURL++, numPkgs++)
224         ;
225
226     rpmMessage(RPMMESS_DEBUG, _("found %d packages\n"), numPkgs);
227
228     pkgURL = xcalloc( (numPkgs + 1), sizeof(*pkgURL) );
229     tmppkgURL = xcalloc( (numPkgs + 1), sizeof(*tmppkgURL) );
230
231     rpmMessage(RPMMESS_DEBUG, _("looking for packages to download\n"));
232     for (fileURL = fileArgv, i = 0; *fileURL; fileURL++) {
233
234         switch (urlIsURL(*fileURL)) {
235         case URL_IS_FTP:
236         case URL_IS_HTTP:
237         {   int myrc;
238             int j;
239             const char *tfn;
240             int argc = 0;
241             const char ** argv = NULL;
242
243             myrc = rpmGlob(*fileURL, &argc, &argv);
244             if (myrc) {
245                 rpmMessage(RPMMESS_ERROR, 
246                         _("skipping %s - rpmGlob failed(%d)\n"),
247                         *fileURL, myrc);
248                 numFailed++;
249                 pkgURL[i] = NULL;
250                 break;
251             }
252             if (argc > 1) {
253                 numPkgs += argc - 1;
254                 pkgURL = xrealloc(pkgURL, (numPkgs + 1) * sizeof(*pkgURL));
255                 tmppkgURL = xrealloc(tmppkgURL, (numPkgs + 1) * sizeof(*tmppkgURL));
256             }
257
258             for (j = 0; j < argc; j++) {
259
260                 if (rpmIsVerbose())
261                     fprintf(stdout, _("Retrieving %s\n"), argv[j]);
262
263                 {   char tfnbuf[64];
264                     strcpy(tfnbuf, "rpm-xfer.XXXXXX");
265                     /*@-unrecog@*/ mktemp(tfnbuf) /*@=unrecog@*/;
266                     tfn = rpmGenPath(rootdir, "%{_tmppath}/", tfnbuf);
267                 }
268
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);
273                 if (myrc < 0) {
274                     rpmMessage(RPMMESS_ERROR, 
275                         _("skipping %s - transfer failed - %s\n"), 
276                         argv[j], ftpStrerror(myrc));
277                     numFailed++;
278                     pkgURL[i] = NULL;
279                     tfn = _free(tfn);
280                 } else {
281                     tmppkgURL[numTmpPkgs++] = pkgURL[i++] = tfn;
282                 }
283             }
284             if (argv) {
285                 for (j = 0; j < argc; j++)
286                     argv[j] = _free(argv[j]);
287                 argv = _free(argv);
288             }
289         }   break;
290         case URL_IS_PATH:
291         default:
292             pkgURL[i++] = *fileURL;
293             break;
294         }
295     }
296     pkgURL[i] = NULL;
297     tmppkgURL[numTmpPkgs] = NULL;
298
299     sourceURL = alloca(sizeof(*sourceURL) * i);
300
301     rpmMessage(RPMMESS_DEBUG, _("retrieved %d packages\n"), numTmpPkgs);
302
303     if (numFailed) goto errxit;
304
305     /**
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.
309      */
310     for (fileURL = pkgURL; *fileURL; fileURL++) {
311         const char * fileName;
312         rpmRC rpmrc;
313
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));
319             if (fd) Fclose(fd);
320             numFailed++;
321             pkgURL[i] = NULL;
322             continue;
323         }
324
325         rpmrc = rpmReadPackageHeader(fd, &h, &isSource, &major, NULL);
326
327         switch (rpmrc) {
328         case RPMRC_BADMAGIC:
329             Fclose(fd);
330             rpmMessage(RPMMESS_ERROR, 
331                         _("%s does not appear to be a RPM package\n"), 
332                         *fileURL);
333             numFailed++;
334             pkgURL[i] = NULL;
335             break;
336         case RPMRC_FAIL:
337         case RPMRC_SHORTREAD:
338         default:
339             rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), *fileURL);
340             numFailed++;
341             pkgURL[i] = NULL;
342             break;
343         case RPMRC_BADSIZE:
344         case RPMRC_OK:
345             if (isSource) {
346                 sourceURL[numSRPMS++] = fileName;
347                 Fclose(fd);
348             } else {
349                 if (!dbIsOpen) {
350                     if (rpmdbOpen(rootdir, &db, mode, 0644)) {
351                         const char *dn;
352                         dn = rpmGetPath( (rootdir ? rootdir : ""), 
353                                         "%{_dbpath}", NULL);
354                         rpmMessage(RPMMESS_ERROR, 
355                                 _("cannot open Packages database in %s\n"), dn);
356                         dn = _free(dn);
357                         numFailed++;
358                         pkgURL[i] = NULL;
359                         break;
360                     }
361                     ts = rpmtransCreateSet(db, rootdir);
362                     dbIsOpen = 1;
363                 }
364
365                 if (defaultReloc) {
366                     const char ** paths;
367                     int c;
368
369                     if (headerGetEntry(h, RPMTAG_PREFIXES, NULL,
370                                        (void **) &paths, &c) && (c == 1)) {
371                         defaultReloc->oldPath = xstrdup(paths[0]);
372                         paths = _free(paths);
373                     } else {
374                         const char * name;
375                         headerNVR(h, &name, NULL, NULL);
376                         rpmMessage(RPMMESS_ERROR, 
377                                _("package %s is not relocateable\n"), name);
378
379                         goto errxit;
380                         /*@notreached@*/
381                     }
382                 }
383
384                 /* On --freshen, verify package is installed and newer */
385                 if (interfaceFlags & INSTALL_FRESHEN) {
386                     rpmdbMatchIterator mi;
387                     const char * name;
388                     Header oldH;
389                     int count;
390
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)
396                             continue;
397                         /* same or newer package already installed */
398                         count = 0;
399                         break;
400                     }
401                     rpmdbFreeIterator(mi);
402                     if (count == 0) {
403                         headerFree(h);
404                         Fclose(fd);
405                         break;  /* XXX out of switch */
406                     }
407                     /* Package is newer than those currently installed. */
408                 }
409
410                 rc = rpmtransAddPackage(ts, h, NULL, fileName,
411                                (interfaceFlags & INSTALL_UPGRADE) != 0,
412                                relocations);
413
414                 headerFree(h);  /* XXX reference held by transaction set */
415                 Fclose(fd);
416
417                 switch(rc) {
418                 case 0:
419                         break;
420                 case 1:
421                         rpmMessage(RPMMESS_ERROR, 
422                             _("error reading from file %s\n"), *fileURL);
423                         goto errxit;
424                         /*@notreached@*/ break;
425                 case 2:
426                         rpmMessage(RPMMESS_ERROR, 
427                             _("file %s requires a newer version of RPM\n"),
428                             *fileURL);
429                         goto errxit;
430                         /*@notreached@*/ break;
431                 }
432
433                 defaultReloc->oldPath = _free(defaultReloc->oldPath);
434
435                 numRPMS++;
436             }
437             break;
438         }
439     }
440
441     rpmMessage(RPMMESS_DEBUG, _("found %d source and %d binary packages\n"), 
442                 numSRPMS, numRPMS);
443
444     if (numFailed) goto errxit;
445
446     if (numRPMS && !(interfaceFlags & INSTALL_NODEPS)) {
447         struct rpmDependencyConflict * conflicts;
448         if (rpmdepCheck(ts, &conflicts, &numConflicts)) {
449             numFailed = numPkgs;
450             stopInstall = 1;
451         }
452
453         if (!stopInstall && conflicts) {
454             rpmMessage(RPMMESS_ERROR, _("failed dependencies:\n"));
455             printDepProblems(stderr, conflicts, numConflicts);
456             rpmdepFreeConflicts(conflicts, numConflicts);
457             numFailed = numPkgs;
458             stopInstall = 1;
459         }
460     }
461
462     if (numRPMS && !(interfaceFlags & INSTALL_NOORDER)) {
463         if (rpmdepOrder(ts)) {
464             numFailed = numPkgs;
465             stopInstall = 1;
466         }
467     }
468
469     if (numRPMS && !stopInstall) {
470         rpmProblemSet probs = NULL;
471
472 #ifdef FANCY_HASH
473         packagesTotal = numRPMS;
474 #endif
475         rpmMessage(RPMMESS_DEBUG, _("installing binary packages\n"));
476         rc = rpmRunTransactions(ts, showProgress, (void *) ((long)notifyFlags), 
477                                     NULL, &probs, transFlags, probFilter);
478
479         if (rc < 0) {
480             numFailed += numRPMS;
481         } else if (rc > 0) {
482             numFailed += rc;
483             rpmProblemSetPrint(stderr, probs);
484         }
485
486         if (probs) rpmProblemSetFree(probs);
487     }
488
489     if (numRPMS && ts) rpmtransFree(ts);
490
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));
497                 if (fd) Fclose(fd);
498                 continue;
499             }
500
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++;
505             }
506
507             Fclose(fd);
508         }
509     }
510
511     for (i = 0; i < numTmpPkgs; i++) {
512         Unlink(tmppkgURL[i]);
513         tmppkgURL[i] = _free(tmppkgURL[i]);
514     }
515     tmppkgURL = _free(tmppkgURL);
516     pkgURL = _free(pkgURL);
517
518     /* FIXME how do we close our various fd's? */
519
520     if (dbIsOpen) rpmdbClose(db);
521
522     return numFailed;
523
524 errxit:
525     if (numRPMS && ts) rpmtransFree(ts);
526     if (tmppkgURL) {
527         for (i = 0; i < numTmpPkgs; i++)
528             tmppkgURL[i] = _free(tmppkgURL[i]);
529         tmppkgURL = _free(tmppkgURL);
530     }
531     pkgURL = _free(pkgURL);
532     if (dbIsOpen) rpmdbClose(db);
533     return numPkgs;
534 }
535
536 int rpmErase(const char * rootdir, const char ** argv,
537                 rpmtransFlags transFlags, 
538                 rpmEraseInterfaceFlags interfaceFlags)
539 {
540     rpmdb db;
541     int mode;
542     int count;
543     const char ** arg;
544     int numFailed = 0;
545     rpmTransactionSet ts;
546     struct rpmDependencyConflict * conflicts;
547     int numConflicts;
548     int stopUninstall = 0;
549     int numPackages = 0;
550     rpmProblemSet probs;
551
552     if (transFlags & RPMTRANS_FLAG_TEST) 
553         mode = O_RDONLY;
554     else
555         mode = O_RDWR | O_EXCL;
556         
557     if (rpmdbOpen(rootdir, &db, mode, 0644)) {
558         const char *dn;
559         dn = rpmGetPath( (rootdir ? rootdir : ""), "%{_dbpath}", NULL);
560         rpmMessage(RPMMESS_ERROR, _("cannot open %s/packages.rpm\n"), dn);
561         dn = _free(dn);
562         return -1;
563     }
564
565     ts = rpmtransCreateSet(db, rootdir);
566     for (arg = argv; *arg; arg++) {
567         rpmdbMatchIterator mi;
568
569         /* XXX HACK to get rpmdbFindByLabel out of the API */
570         mi = rpmdbInitIterator(db, RPMDBI_LABEL, *arg, 0);
571         count = rpmdbGetIteratorCount(mi);
572         if (count <= 0) {
573             rpmMessage(RPMMESS_ERROR, _("package %s is not installed\n"), *arg);
574             numFailed++;
575         } else if (!(count == 1 || (interfaceFlags & UNINSTALL_ALLMATCHES))) {
576             rpmMessage(RPMMESS_ERROR, _("\"%s\" specifies multiple packages\n"),
577                         *arg);
578             numFailed++;
579         } else {
580             Header h;   /* XXX iterator owns the reference */
581             while ((h = rpmdbNextIterator(mi)) != NULL) {
582                 unsigned int recOffset = rpmdbGetIteratorOffset(mi);
583                 if (recOffset) {
584                     rpmtransRemovePackage(ts, recOffset);
585                     numPackages++;
586                 }
587             }
588         }
589         rpmdbFreeIterator(mi);
590     }
591
592     if (!(interfaceFlags & UNINSTALL_NODEPS)) {
593         if (rpmdepCheck(ts, &conflicts, &numConflicts)) {
594             numFailed = numPackages;
595             stopUninstall = 1;
596         }
597
598         if (!stopUninstall && conflicts) {
599             rpmMessage(RPMMESS_ERROR, _("removing these packages would break "
600                               "dependencies:\n"));
601             printDepProblems(stderr, conflicts, numConflicts);
602             rpmdepFreeConflicts(conflicts, numConflicts);
603             numFailed += numPackages;
604             stopUninstall = 1;
605         }
606     }
607
608     if (!stopUninstall) {
609         transFlags |= RPMTRANS_FLAG_REVERSE;
610         numFailed += rpmRunTransactions(ts, NULL, NULL, NULL, &probs,
611                                         transFlags, 0);
612     }
613
614     rpmtransFree(ts);
615     rpmdbClose(db);
616
617     return numFailed;
618 }
619
620 int rpmInstallSource(const char * rootdir, const char * arg,
621                 const char ** specFile, char ** cookie)
622 {
623     FD_t fd;
624     int rc;
625
626     fd = Fopen(arg, "r.ufdio");
627     if (fd == NULL || Ferror(fd)) {
628         rpmMessage(RPMMESS_ERROR, _("cannot open %s: %s\n"), arg, Fstrerror(fd));
629         if (fd) Fclose(fd);
630         return 1;
631     }
632
633     if (rpmIsVerbose())
634         fprintf(stdout, _("Installing %s\n"), arg);
635
636     {   rpmRC rpmrc = rpmInstallSourcePackage(rootdir, fd, specFile, NULL, NULL,
637                                  cookie);
638         rc = (rpmrc == RPMRC_OK ? 0 : 1);
639     }
640     if (rc != 0) {
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);
646     }
647
648     Fclose(fd);
649
650     return rc;
651 }