1646da3dc18d70f11a9fd3290b51aef7ad814869
[platform/upstream/libsolv.git] / examples / solv.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /* solv, a little software installer demoing the sat solver library */
9
10 /* things available in the library but missing from solv:
11  * - vendor policy loading
12  * - soft locks file handling
13  * - multi version handling
14  */
15
16 #define _GNU_SOURCE
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <dirent.h>
21 #include <fnmatch.h>
22 #include <unistd.h>
23 #include <zlib.h>
24 #include <fcntl.h>
25 #include <assert.h>
26 #include <sys/utsname.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <time.h>
30 #include <sys/time.h>
31
32 #include <sys/socket.h>
33 #include <netdb.h>
34 #include <poll.h>
35 #include <errno.h>
36
37 #include "pool.h"
38 #include "poolarch.h"
39 #include "repo.h"
40 #include "evr.h"
41 #include "policy.h"
42 #include "util.h"
43 #include "solver.h"
44 #include "solverdebug.h"
45 #include "chksum.h"
46 #include "repo_solv.h"
47
48 #include "repo_write.h"
49 #include "repo_rpmdb.h"
50 #include "repo_products.h"
51 #include "repo_rpmmd.h"
52 #include "repo_susetags.h"
53 #include "repo_repomdxml.h"
54 #include "repo_updateinfoxml.h"
55 #include "repo_deltainfoxml.h"
56 #include "repo_content.h"
57 #include "pool_fileconflicts.h"
58
59
60 #ifdef FEDORA
61 # define REPOINFO_PATH "/etc/yum.repos.d"
62 #else
63 # define REPOINFO_PATH "/etc/zypp/repos.d"
64 # define PRODUCTS_PATH "/etc/products.d"
65 # define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
66 #endif
67
68 #define SOLVCACHE_PATH "/var/cache/solv"
69
70 #define METADATA_EXPIRE (60 * 90)
71
72 struct repoinfo {
73   Repo *repo;
74
75   char *alias;
76   char *name;
77   int enabled;
78   int autorefresh;
79   char *baseurl;
80   char *metalink;
81   char *mirrorlist;
82   char *path;
83   int type;
84   int pkgs_gpgcheck;
85   int repo_gpgcheck;
86   int priority;
87   int keeppackages;
88   int metadata_expire;
89
90   unsigned char cookie[32];
91   unsigned char extcookie[32];
92 };
93
94 #ifdef FEDORA
95 char *
96 yum_substitute(Pool *pool, char *line)
97 {
98   char *p, *p2;
99   static char *releaseevr;
100   static char *basearch;
101
102   if (!line)
103     {
104       sat_free(releaseevr);
105       releaseevr = 0;
106       sat_free(basearch);
107       basearch = 0;
108       return 0;
109     }
110   p = line;
111   while ((p2 = strchr(p, '$')) != 0)
112     {
113       if (!strncmp(p2, "$releasever", 11))
114         {
115           if (!releaseevr)
116             {
117               Queue q;
118         
119               queue_init(&q);
120               rpm_installedrpmdbids(0, "Providename", "redhat-release", &q);
121               if (q.count)
122                 {
123                   void *handle, *state = 0;
124                   char *p;
125                   handle = rpm_byrpmdbid(q.elements[0], 0, &state);
126                   releaseevr = rpm_query(handle, SOLVABLE_EVR);
127                   rpm_byrpmdbid(0, 0, &state);
128                   if ((p = strchr(releaseevr, '-')) != 0)
129                     *p = 0;
130                 }
131               queue_free(&q);
132               if (!releaseevr)
133                 releaseevr = strdup("?");
134             }
135           *p2 = 0;
136           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
137           p2 = p + (p2 - line);
138           line = p;
139           p = p2 + strlen(releaseevr);
140           continue;
141         }
142       if (!strncmp(p2, "$basearch", 9))
143         {
144           if (!basearch)
145             {
146               struct utsname un;
147               if (uname(&un))
148                 {
149                   perror("uname");
150                   exit(1);
151                 }
152               basearch = strdup(un.machine);
153               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
154                 basearch[1] = '3';
155             }
156           *p2 = 0;
157           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
158           p2 = p + (p2 - line);
159           line = p;
160           p = p2 + strlen(basearch);
161           continue;
162         }
163       p = p2 + 1;
164     }
165   return line;
166 }
167 #endif
168
169 #define TYPE_UNKNOWN    0
170 #define TYPE_SUSETAGS   1
171 #define TYPE_RPMMD      2
172 #define TYPE_PLAINDIR   3
173
174 static int
175 read_repoinfos_sort(const void *ap, const void *bp)
176 {
177   const struct repoinfo *a = ap;
178   const struct repoinfo *b = bp;
179   return strcmp(a->alias, b->alias);
180 }
181
182 struct repoinfo *
183 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
184 {
185   char buf[4096];
186   char buf2[4096], *kp, *vp, *kpe;
187   DIR *dir;
188   FILE *fp;
189   struct dirent *ent;
190   int l, rdlen;
191   struct repoinfo *repoinfos = 0, *cinfo;
192   int nrepoinfos = 0;
193
194   rdlen = strlen(reposdir);
195   dir = opendir(reposdir);
196   if (!dir)
197     {
198       *nrepoinfosp = 0;
199       return 0;
200     }
201   while ((ent = readdir(dir)) != 0)
202     {
203       l = strlen(ent->d_name);
204       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
205         continue;
206       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
207       if ((fp = fopen(buf, "r")) == 0)
208         {
209           perror(buf);
210           continue;
211         }
212       cinfo = 0;
213       while(fgets(buf2, sizeof(buf2), fp))
214         {
215           l = strlen(buf2);
216           if (l == 0)
217             continue;
218           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
219             buf2[--l] = 0;
220           kp = buf2;
221           while (*kp == ' ' || *kp == '\t')
222             kp++;
223           if (!*kp || *kp == '#')
224             continue;
225 #ifdef FEDORA
226           if (strchr(kp, '$'))
227             kp = yum_substitute(pool, kp);
228 #endif
229           if (*kp == '[')
230             {
231               vp = strrchr(kp, ']');
232               if (!vp)
233                 continue;
234               *vp = 0;
235               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
236               cinfo = repoinfos + nrepoinfos++;
237               memset(cinfo, 0, sizeof(*cinfo));
238               cinfo->alias = strdup(kp + 1);
239               cinfo->type = TYPE_RPMMD;
240               cinfo->autorefresh = 1;
241 #ifndef FEDORA
242               cinfo->repo_gpgcheck = 1;
243 #endif
244               cinfo->metadata_expire = METADATA_EXPIRE;
245               continue;
246             }
247           if (!cinfo)
248             continue;
249           vp = strchr(kp, '=');
250           if (!vp)
251             continue;
252           for (kpe = vp - 1; kpe >= kp; kpe--)
253             if (*kpe != ' ' && *kpe != '\t')
254               break;
255           if (kpe == kp)
256             continue;
257           vp++;
258           while (*vp == ' ' || *vp == '\t')
259             vp++;
260           kpe[1] = 0;
261           if (!strcmp(kp, "name"))
262             cinfo->name = strdup(vp);
263           else if (!strcmp(kp, "enabled"))
264             cinfo->enabled = *vp == '0' ? 0 : 1;
265           else if (!strcmp(kp, "autorefresh"))
266             cinfo->autorefresh = *vp == '0' ? 0 : 1;
267           else if (!strcmp(kp, "gpgcheck"))
268             cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
269           else if (!strcmp(kp, "repo_gpgcheck"))
270             cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
271           else if (!strcmp(kp, "baseurl"))
272             cinfo->baseurl = strdup(vp);
273           else if (!strcmp(kp, "mirrorlist"))
274             {
275               if (strstr(vp, "metalink"))
276                 cinfo->metalink = strdup(vp);
277               else
278                 cinfo->mirrorlist = strdup(vp);
279             }
280           else if (!strcmp(kp, "path"))
281             {
282               if (vp && strcmp(vp, "/") != 0)
283                 cinfo->path = strdup(vp);
284             }
285           else if (!strcmp(kp, "type"))
286             {
287               if (!strcmp(vp, "yast2"))
288                 cinfo->type = TYPE_SUSETAGS;
289               else if (!strcmp(vp, "rpm-md"))
290                 cinfo->type = TYPE_RPMMD;
291               else if (!strcmp(vp, "plaindir"))
292                 cinfo->type = TYPE_PLAINDIR;
293               else
294                 cinfo->type = TYPE_UNKNOWN;
295             }
296           else if (!strcmp(kp, "priority"))
297             cinfo->priority = atoi(vp);
298           else if (!strcmp(kp, "keeppackages"))
299             cinfo->keeppackages = *vp == '0' ? 0 : 1;
300         }
301       fclose(fp);
302       cinfo = 0;
303     }
304   closedir(dir);
305   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
306   *nrepoinfosp = nrepoinfos;
307   return repoinfos;
308 }
309
310 void
311 free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
312 {
313   int i;
314   for (i = 0; i < nrepoinfos; i++)
315     {
316       struct repoinfo *cinfo = repoinfos + i;
317       sat_free(cinfo->name);
318       sat_free(cinfo->alias);
319       sat_free(cinfo->path);
320       sat_free(cinfo->metalink);
321       sat_free(cinfo->mirrorlist);
322       sat_free(cinfo->baseurl);
323     }
324   sat_free(repoinfos);
325 }
326
327 static inline int
328 opentmpfile()
329 {
330   char tmpl[100];
331   int fd;
332
333   strcpy(tmpl, "/var/tmp/solvXXXXXX");
334   fd = mkstemp(tmpl);
335   if (fd < 0)
336     {
337       perror("mkstemp");
338       exit(1);
339     }
340   unlink(tmpl);
341   return fd;
342 }
343
344 static int
345 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
346 {
347   char buf[1024];
348   unsigned char *sum;
349   void *h;
350   int l;
351
352   h = sat_chksum_create(chksumtype);
353   if (!h)
354     {
355       printf("%s: unknown checksum type\n", file);
356       return 0;
357     }
358   while ((l = read(fd, buf, sizeof(buf))) > 0)
359     sat_chksum_add(h, buf, l);
360   lseek(fd, 0, SEEK_SET);
361   l = 0;
362   sum = sat_chksum_get(h, &l);
363   if (memcmp(sum, chksum, l))
364     {
365       printf("%s: checksum mismatch\n", file);
366       sat_chksum_free(h, 0);
367       return 0;
368     }
369   sat_chksum_free(h, 0);
370   return 1;
371 }
372
373 void
374 findfastest(char **urls, int nurls)
375 {
376   int i, j, port;
377   int *socks, qc;
378   struct pollfd *fds;
379   char *p, *p2, *q;
380   char portstr[16];
381   struct addrinfo hints, *result;;
382
383   fds = sat_calloc(nurls, sizeof(*fds));
384   socks = sat_calloc(nurls, sizeof(*socks));
385   for (i = 0; i < nurls; i++)
386     {
387       socks[i] = -1;
388       p = strchr(urls[i], '/');
389       if (!p)
390         continue;
391       if (p[1] != '/')
392         continue;
393       p += 2;
394       q = strchr(p, '/');
395       qc = 0;
396       if (q)
397         {
398           qc = *q;
399           *q = 0;
400         }
401       if ((p2 = strchr(p, '@')) != 0)
402         p = p2 + 1;
403       port = 80;
404       if (!strncmp("https:", urls[i], 6))
405         port = 443;
406       else if (!strncmp("ftp:", urls[i], 4))
407         port = 21;
408       if ((p2 = strrchr(p, ':')) != 0)
409         {
410           port = atoi(p2 + 1);
411           if (q)
412             *q = qc;
413           q = p2;
414           qc = *q;
415           *q = 0;
416         }
417       sprintf(portstr, "%d", port);
418       memset(&hints, 0, sizeof(struct addrinfo));
419       hints.ai_family = AF_UNSPEC;
420       hints.ai_socktype = SOCK_STREAM;
421       hints.ai_flags = AI_NUMERICSERV;
422       result = 0;
423       if (!getaddrinfo(p, portstr, &hints, &result))
424         {
425           socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
426           if (socks[i] >= 0)
427             {
428               fcntl(socks[i], F_SETFL, O_NONBLOCK);
429               if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
430                 {
431                   if (errno != EINPROGRESS)
432                     {
433                       close(socks[i]);
434                       socks[i] = -1;
435                     }
436                 }
437             }
438           freeaddrinfo(result);
439         }
440       if (q)
441         *q = qc;
442     }
443   for (;;)
444     {
445       for (i = j = 0; i < nurls; i++)
446         {
447           if (socks[i] < 0)
448             continue;
449           fds[j].fd = socks[i];
450           fds[j].events = POLLOUT;
451           j++;
452         }
453       if (j < 2)
454         {
455           i = j - 1;
456           break;
457         }
458       if (poll(fds, j, 10000) <= 0)
459         {
460           i = -1;       /* something is wrong */
461           break;
462         }
463       for (i = 0; i < j; i++)
464         if ((fds[i].revents & POLLOUT) != 0)
465           {
466             int soe = 0;
467             socklen_t soel = sizeof(int);
468             if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
469               {
470                 /* connect failed, kill socket */
471                 for (j = 0; j < nurls; j++)
472                   if (socks[j] == fds[i].fd)
473                     {
474                       close(socks[j]);
475                       socks[j] = -1;
476                     }
477                 i = j + 1;
478                 break;
479               }
480             break;      /* horray! */
481           }
482       if (i == j + 1)
483         continue;
484       if (i == j)
485         i = -1;         /* something is wrong, no bit was set */
486       break;
487     }
488   /* now i contains the fastest fd index */
489   if (i >= 0)
490     {
491       for (j = 0; j < nurls; j++)
492         if (socks[j] == fds[i].fd)
493           break;
494       if (j != 0)
495         {
496           char *url0 = urls[0];
497           urls[0] = urls[j];
498           urls[j] = url0;
499         }
500     }
501   for (i = j = 0; i < nurls; i++)
502     if (socks[i] >= 0)
503       close(socks[i]);
504   free(socks);
505   free(fds);
506 }
507
508 char *
509 findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
510 {
511   char buf[4096], *bp, *ep;
512   char **urls = 0;
513   int nurls = 0;
514   int i;
515
516   if (chksumtypep)
517     *chksumtypep = 0;
518   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
519     {
520       while (*bp == ' ' || *bp == '\t')
521         bp++;
522       if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
523         {
524           int i;
525
526           bp += 20;
527           memset(chksump, 0, 32);
528           for (i = 0; i < 64; i++)
529             {
530               int c = *bp++;
531               if (c >= '0' && c <= '9')
532                 chksump[i / 2] = chksump[i / 2] * 16 + (c - '0');
533               else if (c >= 'a' && c <= 'f')
534                 chksump[i / 2] = chksump[i / 2] * 16 + (c - ('a' - 10));
535               else if (c >= 'A' && c <= 'F')
536                 chksump[i / 2] = chksump[i / 2] * 16 + (c - ('A' - 10));
537               else
538                 break;
539             }
540           if (i == 64)
541             *chksumtypep = REPOKEY_TYPE_SHA256;
542           continue;
543         }
544       if (strncmp(bp, "<url", 4))
545         continue;
546       bp = strchr(bp, '>');
547       if (!bp)
548         continue;
549       bp++;
550       ep = strstr(bp, "repodata/repomd.xml</url>");
551       if (!ep)
552         continue;
553       *ep = 0;
554       if (strncmp(bp, "http", 4))
555         continue;
556       urls = sat_extend(urls, nurls, 1, sizeof(*urls), 15);
557       urls[nurls++] = strdup(bp);
558     }
559   if (nurls)
560     {
561       if (nurls > 1)
562         findfastest(urls, nurls > 5 ? 5 : nurls);
563       bp = urls[0];
564       urls[0] = 0;
565       for (i = 0; i < nurls; i++)
566         sat_free(urls[i]);
567       sat_free(urls);
568       ep = strchr(bp, '/');
569       if ((ep = strchr(ep + 2, '/')) != 0)
570         {
571           *ep = 0;
572           printf("[using mirror %s]\n", bp);
573           *ep = '/';
574         }
575       return bp;
576     }
577   return 0;
578 }
579
580 char *
581 findmirrorlisturl(FILE *fp)
582 {
583   char buf[4096], *bp, *ep;
584   int i, l;
585   char **urls = 0;
586   int nurls = 0;
587
588   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
589     {
590       while (*bp == ' ' || *bp == '\t')
591         bp++;
592       if (!*bp || *bp == '#')
593         continue;
594       l = strlen(bp);
595       while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
596         bp[--l] = 0;
597       urls = sat_extend(urls, nurls, 1, sizeof(*urls), 15);
598       urls[nurls++] = strdup(bp);
599     }
600   if (nurls)
601     {
602       if (nurls > 1)
603         findfastest(urls, nurls > 5 ? 5 : nurls);
604       bp = urls[0];
605       urls[0] = 0;
606       for (i = 0; i < nurls; i++)
607         sat_free(urls[i]);
608       sat_free(urls);
609       ep = strchr(bp, '/');
610       if ((ep = strchr(ep + 2, '/')) != 0)
611         {
612           *ep = 0;
613           printf("[using mirror %s]\n", bp);
614           *ep = '/';
615         }
616       return bp;
617     }
618   return 0;
619 }
620
621 static ssize_t
622 cookie_gzread(void *cookie, char *buf, size_t nbytes)
623 {
624   return gzread((gzFile *)cookie, buf, nbytes);
625 }
626
627 static int
628 cookie_gzclose(void *cookie)
629 {
630   return gzclose((gzFile *)cookie);
631 }
632
633 FILE *
634 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int *badchecksump)
635 {
636   pid_t pid;
637   int fd, l;
638   int status;
639   char url[4096];
640   const char *baseurl = cinfo->baseurl;
641
642   if (!baseurl)
643     {
644       if (!cinfo->metalink && !cinfo->mirrorlist)
645         return 0;
646       if (file != cinfo->metalink && file != cinfo->mirrorlist)
647         {
648           FILE *fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
649           unsigned char mlchksum[32];
650           Id mlchksumtype = 0;
651           if (!fp)
652             return 0;
653           if (cinfo->metalink)
654             cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
655           else
656             cinfo->baseurl = findmirrorlisturl(fp);
657           fclose(fp);
658           if (!cinfo->baseurl)
659             return 0;
660 #ifdef FEDORA
661           if (strchr(cinfo->baseurl, '$'))
662             {
663               char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
664               free(cinfo->baseurl);
665               cinfo->baseurl = strdup(b);
666             }
667 #endif
668           if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
669             {
670               chksumtype = mlchksumtype;
671               chksum = mlchksum;
672             }
673           return curlfopen(cinfo, file, uncompress, chksum, chksumtype, badchecksump);
674         }
675       snprintf(url, sizeof(url), "%s", file);
676     }
677   else
678     {
679       l = strlen(baseurl);
680       if (l && baseurl[l - 1] == '/')
681         snprintf(url, sizeof(url), "%s%s", baseurl, file);
682       else
683         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
684     }
685   fd = opentmpfile();
686   // printf("url: %s\n", url);
687   if ((pid = fork()) == (pid_t)-1)
688     {
689       perror("fork");
690       exit(1);
691     }
692   if (pid == 0)
693     {
694       if (fd != 1)
695         {
696           dup2(fd, 1);
697           close(fd);
698         }
699       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
700       perror("curl");
701       _exit(0);
702     }
703   status = 0;
704   while (waitpid(pid, &status, 0) != pid)
705     ;
706   if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
707     {
708       /* empty file */
709       close(fd);
710       return 0;
711     }
712   lseek(fd, 0, SEEK_SET);
713   if (status)
714     {
715       printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
716       if (badchecksump)
717         *badchecksump = 1;
718       close(fd);
719       return 0;
720     }
721   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
722     {
723       if (badchecksump)
724         *badchecksump = 1;
725       close(fd);
726       return 0;
727     }
728   if (uncompress)
729     {
730       char tmpl[100];
731       cookie_io_functions_t cio;
732       gzFile *gzf;
733
734       sprintf(tmpl, "/dev/fd/%d", fd);
735       gzf = gzopen(tmpl, "r");
736       close(fd);
737       if (!gzf)
738         {
739           fprintf(stderr, "could not open /dev/fd/%d, /proc not mounted?\n", fd);
740           exit(1);
741         }
742       memset(&cio, 0, sizeof(cio));
743       cio.read = cookie_gzread;
744       cio.close = cookie_gzclose;
745       return fopencookie(gzf, "r", cio);
746     }
747   fcntl(fd, F_SETFD, FD_CLOEXEC);
748   return fdopen(fd, "r");
749 }
750
751 static void
752 cleanupgpg(char *gpgdir)
753 {
754   char cmd[256];
755   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
756   unlink(cmd);
757   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
758   unlink(cmd);
759   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
760   unlink(cmd);
761   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
762   unlink(cmd);
763   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
764   unlink(cmd);
765   rmdir(gpgdir);
766 }
767
768 int
769 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
770 {
771   char *gpgdir;
772   char *keysfile;
773   const char *pubkey;
774   char cmd[256];
775   FILE *kfp;
776   Solvable *s;
777   Id p;
778   off_t posfp, possigfp;
779   int r, nkeys;
780
781   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
782   if (!gpgdir)
783     return 0;
784   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
785   if (!(kfp = fopen(keysfile, "w")) )
786     {
787       cleanupgpg(gpgdir);
788       return 0;
789     }
790   nkeys = 0;
791   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
792     {
793       if (!s->repo)
794         continue;
795       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
796       if (!pubkey || !*pubkey)
797         continue;
798       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
799         break;
800       if (fputc('\n', kfp) == EOF)      /* Just in case... */
801         break;
802       nkeys++;
803     }
804   if (fclose(kfp) || !nkeys)
805     {
806       cleanupgpg(gpgdir);
807       return 0;
808     }
809   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
810   if (system(cmd))
811     {
812       fprintf(stderr, "key import error\n");
813       cleanupgpg(gpgdir);
814       return 0;
815     }
816   unlink(keysfile);
817   posfp = lseek(fileno(fp), 0, SEEK_CUR);
818   lseek(fileno(fp), 0, SEEK_SET);
819   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
820   lseek(fileno(sigfp), 0, SEEK_SET);
821   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
822   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
823   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
824   r = system(cmd);
825   lseek(fileno(sigfp), possigfp, SEEK_SET);
826   lseek(fileno(fp), posfp, SEEK_SET);
827   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
828   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
829   cleanupgpg(gpgdir);
830   return r == 0 ? 1 : 0;
831 }
832
833 #define CHKSUM_IDENT "1.1"
834
835 void
836 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
837 {
838   char buf[4096];
839   void *h = sat_chksum_create(chktype);
840   int l;
841
842   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
843   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
844     sat_chksum_add(h, buf, l);
845   rewind(fp);
846   sat_chksum_free(h, out);
847 }
848
849 void
850 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
851 {
852   void *h = sat_chksum_create(chktype);
853   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
854   sat_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
855   sat_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
856   sat_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
857   sat_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
858   sat_chksum_free(h, out);
859 }
860
861 void
862 setarch(Pool *pool)
863 {
864   struct utsname un;
865   if (uname(&un))
866     {
867       perror("uname");
868       exit(1);
869     }
870   pool_setarch(pool, un.machine);
871 }
872
873 char *calccachepath(Repo *repo, const char *repoext)
874 {
875   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
876   if (repoext)
877     {
878       p = pool_tmpjoin(repo->pool, p, "_", repoext);
879       p = pool_tmpjoin(repo->pool, p, ".solvx", 0);
880     }
881   else
882     p = pool_tmpjoin(repo->pool, p, ".solv", 0);
883   q = p + strlen(SOLVCACHE_PATH) + 1;
884   if (*q == '.')
885     *q = '_';
886   for (; *q; q++)
887     if (*q == '/')
888       *q = '_';
889   return p;
890 }
891
892 int
893 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
894 {
895   FILE *fp;
896   unsigned char mycookie[32];
897   unsigned char myextcookie[32];
898   struct repoinfo *cinfo;
899   int flags;
900
901   cinfo = repo->appdata;
902   if (!(fp = fopen(calccachepath(repo, repoext), "r")))
903     return 0;
904   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
905     {
906       fclose(fp);
907       return 0;
908     }
909   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
910     {
911       fclose(fp);
912       return 0;
913     }
914   if (cinfo && !repoext)
915     {
916       if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
917         {
918           fclose(fp);
919           return 0;
920         }
921     }
922   rewind(fp);
923
924   flags = 0;
925   if (repoext)
926     flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
927   if (repoext && strcmp(repoext, "DL") != 0)
928     flags |= REPO_LOCALPOOL;    /* no local pool for DL so that we can compare IDs */
929
930   if (repo_add_solv_flags(repo, fp, flags))
931     {
932       fclose(fp);
933       return 0;
934     }
935   if (cinfo && !repoext)
936     {
937       memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
938       memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
939     }
940   if (mark)
941     futimes(fileno(fp), 0);     /* try to set modification time */
942   fclose(fp);
943   return 1;
944 }
945
946 void
947 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
948 {
949   FILE *fp;
950   int i, fd;
951   char *tmpl;
952   struct repoinfo *cinfo;
953   int onepiece;
954
955   cinfo = repo->appdata;
956   mkdir(SOLVCACHE_PATH, 0755);
957   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
958   fd = mkstemp(tmpl);
959   if (fd < 0)
960     {
961       free(tmpl);
962       return;
963     }
964   fchmod(fd, 0444);
965   if (!(fp = fdopen(fd, "w")))
966     {
967       close(fd);
968       unlink(tmpl);
969       free(tmpl);
970       return;
971     }
972
973   onepiece = 1;
974   for (i = repo->start; i < repo->end; i++)
975    if (repo->pool->solvables[i].repo != repo)
976      break;
977   if (i < repo->end)
978     onepiece = 0;
979
980   if (!info)
981     repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
982   else if (repoext)
983     repodata_write(info, fp, repo_write_stdkeyfilter, 0);
984   else
985     {
986       int oldnrepodata = repo->nrepodata;
987       repo->nrepodata = 1;      /* XXX: do this right */
988       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
989       repo->nrepodata = oldnrepodata;
990       onepiece = 0;
991     }
992
993   if (!repoext && cinfo)
994     {
995       if (!cinfo->extcookie[0])
996         {
997           /* create the ext cookie and append it */
998           /* we just need some unique ID */
999           struct stat stb;
1000           if (!fstat(fileno(fp), &stb))
1001             {
1002               int i;
1003
1004               calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
1005               for (i = 0; i < 32; i++)
1006                 cinfo->extcookie[i] ^= cookie[i];
1007             }
1008           if (cinfo->extcookie[0] == 0)
1009             cinfo->extcookie[0] = 1;
1010         }
1011       if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
1012         {
1013           fclose(fp);
1014           unlink(tmpl);
1015           free(tmpl);
1016           return;
1017         }
1018     }
1019   /* append our cookie describing the metadata state */
1020   if (fwrite(cookie, 32, 1, fp) != 1)
1021     {
1022       fclose(fp);
1023       unlink(tmpl);
1024       free(tmpl);
1025       return;
1026     }
1027   if (fclose(fp))
1028     {
1029       unlink(tmpl);
1030       free(tmpl);
1031       return;
1032     }
1033   if (onepiece)
1034     {
1035       /* switch to just saved repo to activate paging and save memory */
1036       FILE *fp = fopen(tmpl, "r");
1037       if (fp)
1038         {
1039           if (!repoext)
1040             {
1041               /* main repo */
1042               repo_empty(repo, 1);
1043               if (repo_add_solv_flags(repo, fp, SOLV_ADD_NO_STUBS))
1044                 {
1045                   /* oops, no way to recover from here */
1046                   fprintf(stderr, "internal error\n");
1047                   exit(1);
1048                 }
1049             }
1050           else
1051             {
1052               /* make sure repodata contains complete repo */
1053               /* (this is how repodata_write saves it) */
1054               repodata_extend_block(info, repo->start, repo->end - repo->start);
1055               info->state = REPODATA_LOADING;
1056               /* no need for LOCALPOOL as pool already contains ids */
1057               repo_add_solv_flags(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1058               info->state = REPODATA_AVAILABLE; /* in case the load failed */
1059             }
1060           fclose(fp);
1061         }
1062     }
1063   if (!rename(tmpl, calccachepath(repo, repoext)))
1064     unlink(tmpl);
1065   free(tmpl);
1066 }
1067
1068
1069 static Pool *
1070 read_sigs()
1071 {
1072   Pool *sigpool = pool_create();
1073   Repo *repo = repo_create(sigpool, "rpmdbkeys");
1074   repo_add_rpmdb_pubkeys(repo, 0, 0);
1075   return sigpool;
1076 }
1077
1078
1079 /* repomd helpers */
1080
1081 static inline const char *
1082 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1083 {
1084   Pool *pool = repo->pool;
1085   Dataiterator di;
1086   const char *filename;
1087
1088   filename = 0;
1089   *chksump = 0;
1090   *chksumtypep = 0;
1091   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1092   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1093   if (dataiterator_step(&di))
1094     {
1095       dataiterator_setpos_parent(&di);
1096       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1097       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
1098     }
1099   dataiterator_free(&di);
1100   if (filename && !*chksumtypep)
1101     {
1102       printf("no %s file checksum!\n", what);
1103       filename = 0;
1104     }
1105   return filename;
1106 }
1107
1108 int
1109 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
1110 {
1111   Pool *pool = repo->pool;
1112   Dataiterator di;
1113   Id chksumtype, handle;
1114   const unsigned char *chksum;
1115   const char *filename;
1116
1117   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1118   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1119   if (!dataiterator_step(&di))
1120     {
1121       dataiterator_free(&di);
1122       return 0;
1123     }
1124   if (!strcmp(what, "prestodelta"))
1125     what = "deltainfo";
1126   dataiterator_setpos_parent(&di);
1127   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1128   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
1129   if (!filename || !chksum)
1130     {
1131       dataiterator_free(&di);
1132       return 0;
1133     }
1134   handle = repodata_new_handle(data);
1135   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1136   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1137   if (chksumtype)
1138     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1139   if (!strcmp(what, "deltainfo"))
1140     {
1141       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1142       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1143     }
1144   if (!strcmp(what, "filelists"))
1145     {
1146       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1147       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1148     }
1149   dataiterator_free(&di);
1150   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1151   return 1;
1152 }
1153
1154
1155 /* susetags helpers */
1156
1157 static inline const char *
1158 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1159 {
1160   Pool *pool = repo->pool;
1161   Dataiterator di;
1162   const char *filename;
1163
1164   filename = 0;
1165   *chksump = 0;
1166   *chksumtypep = 0;
1167   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1168   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1169   if (dataiterator_step(&di))
1170     {
1171       dataiterator_setpos_parent(&di);
1172       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1173       filename = what;
1174     }
1175   dataiterator_free(&di);
1176   if (filename && !*chksumtypep)
1177     {
1178       printf("no %s file checksum!\n", what);
1179       filename = 0;
1180     }
1181   return filename;
1182 }
1183
1184 static Id susetags_langtags[] = {
1185   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1186   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1187   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1188   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1189   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1190   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1191   0, 0
1192 };
1193
1194 void
1195 susetags_add_ext(Repo *repo, Repodata *data)
1196 {
1197   Pool *pool = repo->pool;
1198   Dataiterator di;
1199   char ext[3];
1200   Id handle, filechksumtype;
1201   const unsigned char *filechksum;
1202   int i;
1203
1204   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1205   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1206   while (dataiterator_step(&di))
1207     {
1208       if (strncmp(di.kv.str, "packages.", 9) != 0)
1209         continue;
1210       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1211         continue;
1212       ext[0] = di.kv.str[9];
1213       ext[1] = di.kv.str[10];
1214       ext[2] = 0;
1215       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1216         continue;
1217       handle = repodata_new_handle(data);
1218       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1219       if (filechksumtype)
1220         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1221       if (!strcmp(ext, "DU"))
1222         {
1223           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1224           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1225         }
1226       if (!strcmp(ext, "FL"))
1227         {
1228           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1229           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1230         }
1231       else
1232         {
1233           for (i = 0; susetags_langtags[i]; i += 2)
1234             {
1235               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1236               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1237             }
1238         }
1239       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1240     }
1241   dataiterator_free(&di);
1242 }
1243
1244
1245 static inline int
1246 iscompressed(const char *name)
1247 {
1248   int l = strlen(name);
1249   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
1250 }
1251
1252
1253 /* load callback */
1254
1255 int
1256 load_stub(Pool *pool, Repodata *data, void *dp)
1257 {
1258   const char *filename, *descrdir, *repomdtype;
1259   const unsigned char *filechksum;
1260   Id filechksumtype;
1261   struct repoinfo *cinfo;
1262   FILE *fp;
1263   Id defvendor;
1264   char ext[3];
1265
1266   cinfo = data->repo->appdata;
1267
1268   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1269   if (filename)
1270     {
1271       /* susetags load */
1272       ext[0] = filename[9];
1273       ext[1] = filename[10];
1274       ext[2] = 0;
1275 #if 1
1276       printf("[%s:%s", data->repo->name, ext);
1277 #endif
1278       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1279         {
1280           printf(" cached]\n"); fflush(stdout);
1281           return 1;
1282         }
1283 #if 1
1284       printf(" fetching]\n"); fflush(stdout);
1285 #endif
1286       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1287       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
1288       if (!descrdir)
1289         descrdir = "suse/setup/descr";
1290       filechksumtype = 0;
1291       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1292       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1293         return 0;
1294       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1295       fclose(fp);
1296       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1297       return 1;
1298     }
1299
1300   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1301   if (repomdtype)
1302     {
1303       if (!strcmp(repomdtype, "filelists"))
1304         strcpy(ext, "FL");
1305       else if (!strcmp(repomdtype, "deltainfo"))
1306         strcpy(ext, "DL");
1307       else
1308         return 0;
1309 #if 1
1310       printf("[%s:%s", data->repo->name, ext);
1311 #endif
1312       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1313         {
1314           printf(" cached]\n");fflush(stdout);
1315           return 1;
1316         }
1317       printf(" fetching]\n"); fflush(stdout);
1318       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1319       filechksumtype = 0;
1320       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1321       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1322         return 0;
1323       if (!strcmp(ext, "FL"))
1324         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1325       else if (!strcmp(ext, "DL"))
1326         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1327       fclose(fp);
1328       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1329       return 1;
1330     }
1331
1332   return 0;
1333 }
1334
1335 static unsigned char installedcookie[32];
1336
1337 void
1338 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1339 {
1340   Repo *repo;
1341   struct repoinfo *cinfo;
1342   int i;
1343   FILE *fp;
1344   FILE *sigfp;
1345   const char *filename;
1346   const unsigned char *filechksum;
1347   Id filechksumtype;
1348   const char *descrdir;
1349   int defvendor;
1350   struct stat stb;
1351   Pool *sigpool = 0;
1352   Repodata *data;
1353   int badchecksum;
1354   int dorefresh;
1355
1356   repo = repo_create(pool, "@System");
1357   printf("rpm database:");
1358   if (stat("/var/lib/rpm/Packages", &stb))
1359     memset(&stb, 0, sizeof(&stb));
1360   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, installedcookie);
1361   if (usecachedrepo(repo, 0, installedcookie, 0))
1362     printf(" cached\n");
1363   else
1364     {
1365       FILE *ofp;
1366       printf(" reading\n");
1367       int done = 0;
1368
1369 #ifdef PRODUCTS_PATH
1370       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1371 #endif
1372       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1373         {
1374           Repo *ref = repo_create(pool, "@System.old");
1375           if (!repo_add_solv(ref, ofp))
1376             {
1377               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1378               done = 1;
1379             }
1380           fclose(ofp);
1381           repo_free(ref, 1);
1382         }
1383       if (!done)
1384         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1385       writecachedrepo(repo, 0, 0, installedcookie);
1386     }
1387   pool_set_installed(pool, repo);
1388
1389   for (i = 0; i < nrepoinfos; i++)
1390     {
1391       cinfo = repoinfos + i;
1392       if (!cinfo->enabled)
1393         continue;
1394
1395       repo = repo_create(pool, cinfo->alias);
1396       cinfo->repo = repo;
1397       repo->appdata = cinfo;
1398       repo->priority = 99 - cinfo->priority;
1399
1400       dorefresh = cinfo->autorefresh;
1401       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1402         {
1403           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1404             dorefresh = 0;
1405         }
1406       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1407         {
1408           printf("repo '%s':", cinfo->alias);
1409           printf(" cached\n");
1410           continue;
1411         }
1412       badchecksum = 0;
1413       switch (cinfo->type)
1414         {
1415         case TYPE_RPMMD:
1416           printf("rpmmd repo '%s':", cinfo->alias);
1417           fflush(stdout);
1418           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1419             {
1420               printf(" no repomd.xml file, skipped\n");
1421               repo_free(repo, 1);
1422               cinfo->repo = 0;
1423               break;
1424             }
1425           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1426           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1427             {
1428               printf(" cached\n");
1429               fclose(fp);
1430               break;
1431             }
1432           if (cinfo->repo_gpgcheck)
1433             {
1434               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1435               if (!sigfp)
1436                 {
1437                   printf(" unsigned, skipped\n");
1438                   fclose(fp);
1439                   break;
1440                 }
1441               if (!sigpool)
1442                 sigpool = read_sigs();
1443               if (!checksig(sigpool, fp, sigfp))
1444                 {
1445                   printf(" checksig failed, skipped\n");
1446                   fclose(sigfp);
1447                   fclose(fp);
1448                   break;
1449                 }
1450               fclose(sigfp);
1451             }
1452           repo_add_repomdxml(repo, fp, 0);
1453           fclose(fp);
1454           printf(" fetching\n");
1455           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1456           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1457             {
1458               repo_add_rpmmd(repo, fp, 0, 0);
1459               fclose(fp);
1460             }
1461           if (badchecksum)
1462             break;      /* hopeless */
1463
1464           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1465           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1466             {
1467               repo_add_updateinfoxml(repo, fp, 0);
1468               fclose(fp);
1469             }
1470
1471           data = repo_add_repodata(repo, 0);
1472           if (!repomd_add_ext(repo, data, "deltainfo"))
1473             repomd_add_ext(repo, data, "prestodelta");
1474           repomd_add_ext(repo, data, "filelists");
1475           repodata_internalize(data);
1476           if (!badchecksum)
1477             writecachedrepo(repo, 0, 0, cinfo->cookie);
1478           repodata_create_stubs(repo_last_repodata(repo));
1479           break;
1480
1481         case TYPE_SUSETAGS:
1482           printf("susetags repo '%s':", cinfo->alias);
1483           fflush(stdout);
1484           descrdir = 0;
1485           defvendor = 0;
1486           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1487             {
1488               printf(" no content file, skipped\n");
1489               repo_free(repo, 1);
1490               cinfo->repo = 0;
1491               break;
1492             }
1493           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1494           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1495             {
1496               printf(" cached\n");
1497               fclose(fp);
1498               break;
1499             }
1500           if (cinfo->repo_gpgcheck)
1501             {
1502               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1503               if (!sigfp)
1504                 {
1505                   printf(" unsigned, skipped\n");
1506                   fclose(fp);
1507                   break;
1508                 }
1509               if (sigfp)
1510                 {
1511                   if (!sigpool)
1512                     sigpool = read_sigs();
1513                   if (!checksig(sigpool, fp, sigfp))
1514                     {
1515                       printf(" checksig failed, skipped\n");
1516                       fclose(sigfp);
1517                       fclose(fp);
1518                       break;
1519                     }
1520                   fclose(sigfp);
1521                 }
1522             }
1523           repo_add_content(repo, fp, 0);
1524           fclose(fp);
1525           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1526           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1527           if (!descrdir)
1528             descrdir = "suse/setup/descr";
1529           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1530           if (!filename)
1531             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1532           if (!filename)
1533             {
1534               printf(" no packages file entry, skipped\n");
1535               break;
1536             }
1537           printf(" fetching\n");
1538           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1539             break;      /* hopeless */
1540           repo_add_susetags(repo, fp, defvendor, 0, 0);
1541           fclose(fp);
1542           data = repo_add_repodata(repo, 0);
1543           susetags_add_ext(repo, data);
1544           repodata_internalize(data);
1545           if (!badchecksum)
1546             writecachedrepo(repo, 0, 0, cinfo->cookie);
1547           repodata_create_stubs(repo_last_repodata(repo));
1548           break;
1549         default:
1550           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1551           repo_free(repo, 1);
1552           cinfo->repo = 0;
1553           break;
1554         }
1555     }
1556   if (sigpool)
1557     pool_free(sigpool);
1558 }
1559
1560
1561 int
1562 str2archid(Pool *pool, char *arch)
1563 {
1564   Id id;
1565   if (!*arch)
1566     return 0;
1567   id = str2id(pool, arch, 0);
1568   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1569     return id;
1570   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1571     return 0;
1572   return id;
1573 }
1574
1575 int
1576 depglob(Pool *pool, char *name, Queue *job)
1577 {
1578   Id p, pp;
1579   Id id = str2id(pool, name, 0);
1580   int i, match = 0;
1581
1582   if (id)
1583     {
1584       FOR_PROVIDES(p, pp, id)
1585         {
1586           Solvable *s = pool->solvables + p;
1587           match = 1;
1588           if (s->name == id)
1589             {
1590               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1591               return 1;
1592             }
1593         }
1594       if (match)
1595         {
1596           printf("[using capability match for '%s']\n", name);
1597           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1598           return 1;
1599         }
1600     }
1601
1602   if (strpbrk(name, "[*?") == 0)
1603     return 0;
1604
1605   /* looks like a name glob. hard work. */
1606   for (p = 1; p < pool->nsolvables; p++)
1607     {
1608       Solvable *s = pool->solvables + p;
1609       if (!s->repo || !pool_installable(pool, s))
1610         continue;
1611       id = s->name;
1612       if (fnmatch(name, id2str(pool, id), 0) == 0)
1613         {
1614           for (i = 0; i < job->count; i += 2)
1615             if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1616               break;
1617           if (i == job->count)
1618             queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1619           match = 1;
1620         }
1621     }
1622   if (match)
1623     return 1;
1624   /* looks like a dep glob. really hard work. */
1625   for (id = 1; id < pool->ss.nstrings; id++)
1626     {
1627       if (!pool->whatprovides[id])
1628         continue;
1629       if (fnmatch(name, id2str(pool, id), 0) == 0)
1630         {
1631           if (!match)
1632             printf("[using capability match for '%s']\n", name);
1633           for (i = 0; i < job->count; i += 2)
1634             if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1635               break;
1636           if (i == job->count)
1637             queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1638           match = 1;
1639         }
1640     }
1641   if (match)
1642     return 1;
1643   return 0;
1644 }
1645
1646 void
1647 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1648 {
1649   int i;
1650   for (i = 0; i < job->count; i += 2)
1651     {
1652       if (job->elements[i] != SOLVER_SOLVABLE_NAME && job->elements[i] != SOLVER_SOLVABLE_PROVIDES)
1653         continue;
1654       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1655     }
1656 }
1657
1658 int
1659 limitevr(Pool *pool, char *evr, Queue *job, Id archid)
1660 {
1661   Queue mq;
1662   Id p, pp, evrid;
1663   int matched = 0;
1664   int i, j;
1665   Solvable *s;
1666   const char *sevr;
1667
1668   queue_init(&mq);
1669   for (i = 0; i < job->count; i += 2)
1670     {
1671       queue_empty(&mq);
1672       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1673         {
1674           s = pool_id2solvable(pool, p);
1675           if (archid && s->arch != archid)
1676             continue;
1677           sevr = id2str(pool, s->evr);
1678           if (!strchr(evr, ':') && strchr(sevr, ':'))
1679             sevr = strchr(sevr, ':') + 1;
1680           if (evrcmp_str(pool, sevr, evr, EVRCMP_MATCH) == 0)
1681              queue_push(&mq, p);
1682         }
1683       if (mq.count)
1684         {
1685           if (!matched && i)
1686             {
1687               queue_deleten(job, 0, i);
1688               i = 0;
1689             }
1690           matched = 1;
1691           /* if all solvables have the same evr */
1692           s = pool_id2solvable(pool, mq.elements[0]);
1693           evrid = s->evr;
1694           for (j = 0; j < mq.count; j++)
1695             {
1696               s = pool_id2solvable(pool, mq.elements[j]);
1697               if (s->evr != evrid)
1698                 break;
1699             }
1700           if (j == mq.count && j > 1)
1701             {
1702               prune_to_best_arch(pool, &mq);
1703               // prune_to_highest_prio(pool, &mq);
1704               mq.count = 1;
1705             }
1706           if (mq.count > 1)
1707             {
1708               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1709               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1710             }
1711           else
1712             {
1713               job->elements[i] = SOLVER_SOLVABLE;
1714               job->elements[i + 1] = mq.elements[0];
1715             }
1716         }
1717       else if (matched)
1718         {
1719           queue_deleten(job, i, 2);
1720           i -= 2;
1721         }
1722     }
1723   queue_free(&mq);
1724   if (matched)
1725     return 1;
1726   if (!archid)
1727     {
1728       char *r;
1729       if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1730         {
1731           *r = 0;
1732           if (limitevr(pool, evr, job, archid))
1733             {
1734               *r = '.';
1735               return 1;
1736             }
1737           *r = '.';
1738         }
1739     }
1740   return 0;
1741 }
1742
1743 void
1744 mkselect(Pool *pool, int mode, char *name, Queue *job)
1745 {
1746   char *r, *r2;
1747   Id archid;
1748
1749   if (*name == '/')
1750     {
1751       Dataiterator di;
1752       Queue q;
1753       int match = 0;
1754
1755       queue_init(&q);
1756       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1757       while (dataiterator_step(&di))
1758         {
1759           Solvable *s = pool->solvables + di.solvid;
1760           if (!s->repo || !pool_installable(pool, s))
1761             continue;
1762           queue_push(&q, di.solvid);
1763           dataiterator_skip_solvable(&di);
1764         }
1765       dataiterator_free(&di);
1766       if (q.count)
1767         {
1768           printf("[using file list match for '%s']\n", name);
1769           match = 1;
1770           if (q.count > 1)
1771             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1772           else
1773             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1774         }
1775       queue_free(&q);
1776       if (match)
1777         return;
1778     }
1779   if ((r = strpbrk(name, "<=>")) != 0)
1780     {
1781       /* relation case, support:
1782        * depglob rel
1783        * depglob.rpm rel
1784        */
1785       int rflags = 0;
1786       int nend = r - name;
1787       for (; *r; r++)
1788         {
1789           if (*r == '<')
1790             rflags |= REL_LT;
1791           else if (*r == '=')
1792             rflags |= REL_EQ;
1793           else if (*r == '>')
1794             rflags |= REL_GT;
1795           else
1796             break;
1797         }
1798       while (*r && *r == ' ' && *r == '\t')
1799         r++;
1800       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1801         nend--;
1802       name[nend] = 0;
1803       if (!*name || !*r)
1804         {
1805           fprintf(stderr, "bad relation\n");
1806           exit(1);
1807         }
1808       if (depglob(pool, name, job))
1809         {
1810           addrelation(pool, job, rflags, str2id(pool, r, 1));
1811           return;
1812         }
1813       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1814         {
1815           *r2 = 0;
1816           if (depglob(pool, name, job))
1817             {
1818               *r2 = '.';
1819               addrelation(pool, job, REL_ARCH, archid);
1820               addrelation(pool, job, rflags, str2id(pool, r, 1));
1821               return;
1822             }
1823           *r2 = '.';
1824         }
1825     }
1826   else
1827     {
1828       /* no relation case, support:
1829        * depglob
1830        * depglob.arch
1831        * depglob-version-release
1832        * depglob-version-release.arch
1833        */
1834       if (depglob(pool, name, job))
1835         return;
1836       archid = 0;
1837       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1838         {
1839           *r = 0;
1840           if (depglob(pool, name, job))
1841             {
1842               *r = '.';
1843               addrelation(pool, job, REL_ARCH, archid);
1844               return;
1845             }
1846           *r = '.';
1847         }
1848       if ((r = strrchr(name, '-')) != 0)
1849         {
1850           *r = 0;
1851           if (depglob(pool, name, job))
1852             {
1853               /* have just the version */
1854               *r = '-';
1855               if (limitevr(pool, r + 1, job, 0))
1856                 return;
1857             }
1858           if ((r2 = strrchr(name, '-')) != 0)
1859             {
1860               *r = '-';
1861               *r2 = 0;
1862               if (depglob(pool, name, job))
1863                 {
1864                   *r2 = '-';
1865                   if (limitevr(pool, r2 + 1, job, 0))
1866                     return;
1867                 }
1868               *r2 = '-';
1869             }
1870           *r = '-';
1871         }
1872     }
1873   fprintf(stderr, "nothing matches '%s'\n", name);
1874   exit(1);
1875 }
1876
1877
1878 int
1879 yesno(const char *str)
1880 {
1881   char inbuf[128], *ip;
1882
1883   for (;;)
1884     {
1885       printf("%s", str);
1886       fflush(stdout);
1887       *inbuf = 0;
1888       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1889         {
1890           printf("Abort.\n");
1891           exit(1);
1892         }
1893       while (*ip == ' ' || *ip == '\t')
1894         ip++;
1895       if (*ip == 'q')
1896         {
1897           printf("Abort.\n");
1898           exit(1);
1899         }
1900       if (*ip == 'y' || *ip == 'n')
1901         return *ip == 'y' ? 1 : 0;
1902     }
1903 }
1904
1905 struct fcstate {
1906   FILE **newpkgsfps;
1907   Queue *checkq;
1908   int newpkgscnt;
1909   void *rpmdbstate;
1910 };
1911
1912 static void *
1913 fileconflict_cb(Pool *pool, Id p, void *cbdata)
1914 {
1915   struct fcstate *fcstate = cbdata;
1916   Solvable *s;
1917   Id rpmdbid;
1918   int i;
1919   FILE *fp;
1920
1921   if (!p)
1922     {
1923       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1924       return 0;
1925     }
1926   s = pool_id2solvable(pool, p);
1927   if (pool->installed && s->repo == pool->installed)
1928     {
1929       if (!s->repo->rpmdbid)
1930         return 0;
1931       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1932       if (!rpmdbid)
1933         return 0;
1934        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1935     }
1936   for (i = 0; i < fcstate->newpkgscnt; i++)
1937     if (fcstate->checkq->elements[i] == p)
1938       break;
1939   if (i == fcstate->newpkgscnt)
1940     return 0;
1941   fp = fcstate->newpkgsfps[i];
1942   if (!fp)
1943     return 0;
1944   rewind(fp);
1945   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1946 }
1947
1948 void
1949 runrpm(const char *arg, const char *name, int dupfd3)
1950 {
1951   pid_t pid;
1952   int status;
1953
1954   if ((pid = fork()) == (pid_t)-1)
1955     {
1956       perror("fork");
1957       exit(1);
1958     }
1959   if (pid == 0)
1960     {
1961       if (dupfd3 != -1 && dupfd3 != 3)
1962         {
1963           dup2(dupfd3, 3);
1964           close(dupfd3);
1965         }
1966       if (dupfd3 != -1)
1967         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1968       if (strcmp(arg, "-e") == 0)
1969         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1970       else
1971         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1972       perror("rpm");
1973       _exit(0);
1974     }
1975   while (waitpid(pid, &status, 0) != pid)
1976     ;
1977   if (status)
1978     {
1979       printf("rpm failed\n");
1980       exit(1);
1981     }
1982 }
1983
1984 static Id
1985 nscallback(Pool *pool, void *data, Id name, Id evr)
1986 {
1987   if (name == NAMESPACE_PRODUCTBUDDY)
1988     {    
1989       /* SUSE specific hack: each product has an associated rpm */
1990       Solvable *s = pool->solvables + evr; 
1991       Id p, pp, cap; 
1992       
1993       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1994       if (!cap)
1995         return 0;
1996       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1997       if (!cap)
1998         return 0;
1999       FOR_PROVIDES(p, pp, cap) 
2000         {
2001           Solvable *ps = pool->solvables + p; 
2002           if (ps->repo == s->repo && ps->arch == s->arch)
2003             break;
2004         }
2005       return p;
2006     }
2007   return 0;
2008 }
2009
2010 #ifdef SOFTLOCKS_PATH
2011
2012 void
2013 addsoftlocks(Pool *pool, Queue *job)
2014 {
2015   FILE *fp;
2016   Id type, id, p, pp;
2017   char *bp, *ep, buf[4096];
2018
2019   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2020     return;
2021   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2022     {
2023       while (*bp == ' ' || *bp == '\t')
2024         bp++;
2025       if (!*bp || *bp == '#')
2026         continue;
2027       for (ep = bp; *ep; ep++)
2028         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2029           break;
2030       *ep = 0;
2031       type = SOLVER_SOLVABLE_NAME;
2032       if (!strncmp(bp, "provides:", 9) && bp[9])
2033         {
2034           type = SOLVER_SOLVABLE_PROVIDES;
2035           bp += 9;
2036         }
2037       id = str2id(pool, bp, 1);
2038       if (pool->installed)
2039         {
2040           FOR_JOB_SELECT(p, pp, type, id)
2041             if (pool->solvables[p].repo == pool->installed)
2042               break;
2043           if (p)
2044             continue;   /* ignore, as it is already installed */
2045         }
2046       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2047     }
2048   fclose(fp);
2049 }
2050
2051 #endif
2052
2053
2054 void
2055 rewrite_repos(Pool *pool, Id *addedfileprovides)
2056 {
2057   Repo *repo;
2058   Repodata *data;
2059   Map providedids;
2060   Queue fileprovidesq;
2061   Id id;
2062   int i, j, n, nprovidedids;
2063   struct repoinfo *cinfo;
2064
2065   map_init(&providedids, pool->ss.nstrings);
2066   queue_init(&fileprovidesq);
2067   for (nprovidedids = 0; (id = addedfileprovides[nprovidedids]) != 0; nprovidedids++)
2068     MAPSET(&providedids, id);
2069   FOR_REPOS(i, repo)
2070     {
2071       /* make sure this repo has just one main repodata */
2072       if (!repo->nrepodata)
2073         continue;
2074       cinfo = repo->appdata;
2075       data = repo->repodata + 0;
2076       if (data->store.pagefd == -1)
2077         continue;
2078       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2079         {
2080           n = 0;
2081           for (j = 0; j < fileprovidesq.count; j++)
2082             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2083               n++;
2084           if (n == nprovidedids)
2085             continue;   /* nothing new added */
2086         }
2087       /* oh my! */
2088       for (j = 0; addedfileprovides[j]; j++)
2089         repodata_add_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[j]);
2090       repodata_internalize(data);
2091       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2092     }
2093   queue_free(&fileprovidesq);
2094   map_free(&providedids);
2095 }
2096
2097 #define MODE_LIST        0
2098 #define MODE_INSTALL     1
2099 #define MODE_ERASE       2
2100 #define MODE_UPDATE      3
2101 #define MODE_DISTUPGRADE 4
2102 #define MODE_VERIFY      5
2103 #define MODE_PATCH       6
2104 #define MODE_INFO        7
2105 #define MODE_REPOLIST    8
2106 #define MODE_SEARCH      9
2107
2108 void
2109 usage(int r)
2110 {
2111   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2112   fprintf(stderr, "\n");
2113   fprintf(stderr, "    distupgrade: replace installed packages with\n");
2114   fprintf(stderr, "                 versions from the repositories\n");
2115   fprintf(stderr, "    erase:       erase installed packages\n");
2116   fprintf(stderr, "    info:        display package information\n");
2117   fprintf(stderr, "    install:     install packages\n");
2118   fprintf(stderr, "    list:        list packages\n");
2119   fprintf(stderr, "    repos:       list enabled repositories\n");
2120   fprintf(stderr, "    search:      search name/summary/description\n");
2121   fprintf(stderr, "    update:      update installed packages\n");
2122   fprintf(stderr, "    verify:      check dependencies of installed packages\n");
2123   fprintf(stderr, "\n");
2124   exit(r);
2125 }
2126
2127 int
2128 main(int argc, char **argv)
2129 {
2130   Pool *pool;
2131   Repo *commandlinerepo = 0;
2132   Id *commandlinepkgs = 0;
2133   Id p, pp;
2134   struct repoinfo *repoinfos;
2135   int nrepoinfos = 0;
2136   int mainmode = 0, mode = 0;
2137   int i, newpkgs;
2138   Queue job, checkq;
2139   Solver *solv = 0;
2140   Transaction *trans;
2141   char inbuf[128], *ip;
2142   int allpkgs = 0;
2143   FILE **newpkgsfps;
2144   struct fcstate fcstate;
2145   Id *addedfileprovides = 0;
2146
2147   argc--;
2148   argv++;
2149   if (!argv[0])
2150     usage(1);
2151   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2152     {
2153       mainmode = MODE_INSTALL;
2154       mode = SOLVER_INSTALL;
2155     }
2156   else if (!strcmp(argv[0], "patch"))
2157     {
2158       mainmode = MODE_PATCH;
2159       mode = SOLVER_INSTALL;
2160     }
2161   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2162     {
2163       mainmode = MODE_ERASE;
2164       mode = SOLVER_ERASE;
2165     }
2166   else if (!strcmp(argv[0], "list"))
2167     {
2168       mainmode = MODE_LIST;
2169       mode = 0;
2170     }
2171   else if (!strcmp(argv[0], "info"))
2172     {
2173       mainmode = MODE_INFO;
2174       mode = 0;
2175     }
2176   else if (!strcmp(argv[0], "search"))
2177     {
2178       mainmode = MODE_SEARCH;
2179       mode = 0;
2180     }
2181   else if (!strcmp(argv[0], "verify"))
2182     {
2183       mainmode = MODE_VERIFY;
2184       mode = SOLVER_VERIFY;
2185     }
2186   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2187     {
2188       mainmode = MODE_UPDATE;
2189       mode = SOLVER_UPDATE;
2190     }
2191   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2192     {
2193       mainmode = MODE_DISTUPGRADE;
2194       mode = SOLVER_UPDATE;
2195     }
2196   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2197     {
2198       mainmode = MODE_REPOLIST;
2199       mode = 0;
2200     }
2201   else
2202     usage(1);
2203
2204   pool = pool_create();
2205 #ifdef FEDORA
2206   pool->obsoleteusescolors = 1;
2207 #endif
2208   pool_setloadcallback(pool, load_stub, 0);
2209   pool->nscallback = nscallback;
2210   // pool_setdebuglevel(pool, 2);
2211   setarch(pool);
2212   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
2213
2214   if (mainmode == MODE_REPOLIST)
2215     {
2216       int j = 1;
2217       for (i = 0; i < nrepoinfos; i++)
2218         {
2219           struct repoinfo *cinfo = repoinfos + i;
2220           if (!cinfo->enabled)
2221             continue;
2222           printf("%d: %-20s %s\n", j++, cinfo->alias, cinfo->name);
2223         }
2224       exit(0);
2225     }
2226
2227   read_repos(pool, repoinfos, nrepoinfos);
2228
2229   if (mainmode == MODE_SEARCH)
2230     {
2231       Dataiterator di;
2232       Map m;
2233       if (argc != 2)
2234         usage(1);
2235       map_init(&m, pool->nsolvables);
2236       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2237       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2238       dataiterator_set_search(&di, 0, 0);
2239       while (dataiterator_step(&di))
2240         MAPSET(&m, di.solvid);
2241       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2242       dataiterator_set_search(&di, 0, 0);
2243       while (dataiterator_step(&di))
2244         MAPSET(&m, di.solvid);
2245       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2246       dataiterator_set_search(&di, 0, 0);
2247       while (dataiterator_step(&di))
2248         MAPSET(&m, di.solvid);
2249       dataiterator_free(&di);
2250
2251       for (p = 1; p < pool->nsolvables; p++)
2252         {
2253           Solvable *s = pool_id2solvable(pool, p);
2254           if (!MAPTST(&m, p))
2255             continue;
2256           printf("  - %s: %s\n", solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2257         }
2258       map_free(&m);
2259       exit(0);
2260     }
2261
2262
2263   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2264     {
2265       for (i = 1; i < argc; i++)
2266         {
2267           int l;
2268           l = strlen(argv[i]);
2269           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2270             continue;
2271           if (access(argv[i], R_OK))
2272             {
2273               perror(argv[i]);
2274               exit(1);
2275             }
2276           if (!commandlinepkgs)
2277             commandlinepkgs = sat_calloc(argc, sizeof(Id));
2278           if (!commandlinerepo)
2279             commandlinerepo = repo_create(pool, "@commandline");
2280           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2281           commandlinepkgs[i] = commandlinerepo->end - 1;
2282         }
2283       if (commandlinerepo)
2284         repo_internalize(commandlinerepo);
2285     }
2286
2287   // FOR_REPOS(i, repo)
2288   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2289   addedfileprovides = 0;
2290   pool_addfileprovides_ids(pool, 0, &addedfileprovides);
2291   if (addedfileprovides && *addedfileprovides)
2292     rewrite_repos(pool, addedfileprovides);
2293   sat_free(addedfileprovides);
2294   pool_createwhatprovides(pool);
2295
2296   queue_init(&job);
2297   for (i = 1; i < argc; i++)
2298     {
2299       Queue job2;
2300       int j;
2301
2302       if (commandlinepkgs && commandlinepkgs[i])
2303         {
2304           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2305           continue;
2306         }
2307       queue_init(&job2);
2308       mkselect(pool, mode, argv[i], &job2);
2309       for (j = 0; j < job2.count; j++)
2310         queue_push(&job, job2.elements[j]);
2311       queue_free(&job2);
2312     }
2313
2314   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2315     {
2316       printf("no package matched\n");
2317       exit(1);
2318     }
2319
2320   if (!job.count)
2321     allpkgs = 1;
2322
2323   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2324     {
2325       /* list mode, no solver needed */
2326       for (i = 0; i < job.count; i += 2)
2327         {
2328           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2329             {
2330               Solvable *s = pool_id2solvable(pool, p);
2331               if (mainmode == MODE_INFO)
2332                 {
2333                   printf("Name:        %s\n", solvable2str(pool, s));
2334                   printf("Repo:        %s\n", s->repo->name);
2335                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2336                   printf("Url:         %s\n", solvable_lookup_str(s, SOLVABLE_URL));
2337                   printf("License:     %s\n", solvable_lookup_str(s, SOLVABLE_LICENSE));
2338                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2339                   printf("\n");
2340                 }
2341               else
2342                 {
2343                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
2344                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2345                   if (sum)
2346                     printf("    %s\n", sum);
2347                 }
2348             }
2349         }
2350       queue_free(&job);
2351       pool_free(pool);
2352       free_repoinfos(repoinfos, nrepoinfos);
2353       sat_free(commandlinepkgs);
2354 #ifdef FEDORA
2355       yum_substitute(pool, 0);
2356 #endif
2357       exit(0);
2358     }
2359
2360   if (mainmode == MODE_PATCH)
2361     {
2362       int pruneyou = 0;
2363       Map installedmap;
2364       Solvable *s;
2365
2366       map_init(&installedmap, pool->nsolvables);
2367       if (pool->installed)
2368         FOR_REPO_SOLVABLES(pool->installed, p, s)
2369           MAPSET(&installedmap, p);
2370
2371       /* install all patches */
2372       for (p = 1; p < pool->nsolvables; p++)
2373         {
2374           const char *type;
2375           int r;
2376           Id p2;
2377
2378           s = pool->solvables + p;
2379           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2380             continue;
2381           FOR_PROVIDES(p2, pp, s->name)
2382             {
2383               Solvable *s2 = pool->solvables + p2;
2384               if (s2->name != s->name)
2385                 continue;
2386               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2387               if (r < 0 || (r == 0 && p > p2))
2388                 break;
2389             }
2390           if (p2)
2391             continue;
2392           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2393           if (type && !strcmp(type, "optional"))
2394             continue;
2395           r = solvable_trivial_installable_map(s, &installedmap, 0);
2396           if (r == -1)
2397             continue;
2398           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2399             {
2400               if (!pruneyou++)
2401                 queue_empty(&job);
2402             }
2403           else if (pruneyou)
2404             continue;
2405           queue_push2(&job, SOLVER_SOLVABLE, p);
2406         }
2407       map_free(&installedmap);
2408     }
2409
2410   // add mode
2411   for (i = 0; i < job.count; i += 2)
2412     {
2413       if (mode == SOLVER_UPDATE)
2414         {
2415           /* make update of not installed packages an install */
2416           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2417             if (pool->installed && pool->solvables[p].repo == pool->installed)
2418               break;
2419           if (!p)
2420             {
2421               job.elements[i] |= SOLVER_INSTALL;
2422               continue;
2423             }
2424         }
2425       job.elements[i] |= mode;
2426     }
2427
2428   // multiversion test
2429   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2430   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2431   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2432
2433 #ifdef SOFTLOCKS_PATH
2434   addsoftlocks(pool, &job);
2435 #endif
2436
2437 rerunsolver:
2438   for (;;)
2439     {
2440       Id problem, solution;
2441       int pcnt, scnt;
2442
2443       solv = solver_create(pool);
2444       solv->ignorealreadyrecommended = 1;
2445       solv->updatesystem = allpkgs && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
2446       solv->dosplitprovides = solv->updatesystem;
2447       solv->fixsystem = allpkgs && (mainmode == MODE_VERIFY);
2448       if (mainmode == MODE_DISTUPGRADE)
2449         {
2450           solv->distupgrade = 1;
2451           solv->allowdowngrade = 1;
2452           solv->allowarchchange = 1;
2453           solv->allowvendorchange = 1;
2454         }
2455       if (mainmode == MODE_ERASE)
2456         solv->allowuninstall = 1;       /* don't nag */
2457
2458       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
2459       solver_solve(solv, &job);
2460       if (!solv->problems.count)
2461         break;
2462       pcnt = solver_problem_count(solv);
2463       printf("Found %d problems:\n", pcnt);
2464       for (problem = 1; problem <= pcnt; problem++)
2465         {
2466           int take = 0;
2467           printf("Problem %d:\n", problem);
2468           solver_printprobleminfo(solv, problem);
2469           printf("\n");
2470           scnt = solver_solution_count(solv, problem);
2471           for (solution = 1; solution <= scnt; solution++)
2472             {
2473               printf("Solution %d:\n", solution);
2474               solver_printsolution(solv, problem, solution);
2475               printf("\n");
2476             }
2477           for (;;)
2478             {
2479               printf("Please choose a solution: ");
2480               fflush(stdout);
2481               *inbuf = 0;
2482               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2483                 {
2484                   printf("Abort.\n");
2485                   exit(1);
2486                 }
2487               while (*ip == ' ' || *ip == '\t')
2488                 ip++;
2489               if (*ip >= '0' && *ip <= '9')
2490                 {
2491                   take = atoi(ip);
2492                   if (take >= 1 && take <= scnt)
2493                     break;
2494                 }
2495               if (*ip == 's')
2496                 {
2497                   take = 0;
2498                   break;
2499                 }
2500               if (*ip == 'q')
2501                 {
2502                   printf("Abort.\n");
2503                   exit(1);
2504                 }
2505             }
2506           if (!take)
2507             continue;
2508           solver_take_solution(solv, problem, take, &job);
2509         }
2510       solver_free(solv);
2511       solv = 0;
2512     }
2513
2514   trans = &solv->trans;
2515   if (!trans->steps.count)
2516     {
2517       printf("Nothing to do.\n");
2518       exit(1);
2519     }
2520   printf("\n");
2521   printf("Transaction summary:\n\n");
2522   solver_printtransaction(solv);
2523
2524 #ifndef FEDORA
2525   if (1)
2526     {
2527       DUChanges duc[4];
2528       int i;
2529
2530       duc[0].path = "/";
2531       duc[1].path = "/usr/share/man";
2532       duc[2].path = "/sbin";
2533       duc[3].path = "/etc";
2534       transaction_calc_duchanges(trans, duc, 4);
2535       for (i = 0; i < 4; i++)
2536         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2537     }
2538 #endif
2539   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
2540   printf("\n");
2541
2542   if (!yesno("OK to continue (y/n)? "))
2543     {
2544       printf("Abort.\n");
2545       exit(1);
2546     }
2547
2548   queue_init(&checkq);
2549   newpkgs = transaction_installedresult(trans, &checkq);
2550   newpkgsfps = 0;
2551
2552   if (newpkgs)
2553     {
2554       int downloadsize = 0;
2555       for (i = 0; i < newpkgs; i++)
2556         {
2557           Solvable *s;
2558
2559           p = checkq.elements[i];
2560           s = pool_id2solvable(pool, p);
2561           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
2562         }
2563       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
2564       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
2565       for (i = 0; i < newpkgs; i++)
2566         {
2567           unsigned int medianr;
2568           char *loc;
2569           Solvable *s;
2570           struct repoinfo *cinfo;
2571           const unsigned char *chksum;
2572           Id chksumtype;
2573           Dataiterator di;
2574
2575           p = checkq.elements[i];
2576           s = pool_id2solvable(pool, p);
2577           if (s->repo == commandlinerepo)
2578             {
2579               loc = solvable_get_location(s, &medianr);
2580               if (!(newpkgsfps[i] = fopen(loc, "r")))
2581                 {
2582                   perror(loc);
2583                   exit(1);
2584                 }
2585               putchar('.');
2586               continue;
2587             }
2588           cinfo = s->repo->appdata;
2589           if (!cinfo)
2590             {
2591               printf("%s: no repository information\n", s->repo->name);
2592               exit(1);
2593             }
2594           loc = solvable_get_location(s, &medianr);
2595           if (!loc)
2596              continue;
2597
2598           if (pool->installed && pool->installed->nsolvables)
2599             {
2600               /* try a delta first */
2601               char *matchname = strdup(id2str(pool, s->name));
2602               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2603               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2604               while (dataiterator_step(&di))
2605                 {
2606                   Id baseevr, op;
2607
2608                   dataiterator_setpos_parent(&di);
2609                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2610                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2611                     continue;
2612                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2613                   FOR_PROVIDES(op, pp, s->name)
2614                     {
2615                       Solvable *os = pool->solvables + op;
2616                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2617                         break;
2618                     }
2619                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2620                     {
2621                       /* base is installed, run sequence check */
2622                       const char *seqname;
2623                       const char *seqevr;
2624                       const char *seqnum;
2625                       const char *seq;
2626                       const char *dloc;
2627                       FILE *fp;
2628                       char cmd[128];
2629                       int newfd;
2630
2631                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2632                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2633                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2634                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2635                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2636 #ifdef FEDORA
2637                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
2638 #else
2639                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
2640 #endif
2641                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
2642                         continue;       /* didn't match */
2643                       /* looks good, download delta */
2644                       chksumtype = 0;
2645                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2646                       if (!chksumtype)
2647                         continue;       /* no way! */
2648                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2649                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2650                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2651                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2652                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2653                         continue;
2654                       /* got it, now reconstruct */
2655                       newfd = opentmpfile();
2656 #ifdef FEDORA
2657                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
2658 #else
2659                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2660 #endif
2661                       fcntl(fileno(fp), F_SETFD, 0);
2662                       if (system(cmd))
2663                         {
2664                           close(newfd);
2665                           fclose(fp);
2666                           continue;
2667                         }
2668                       lseek(newfd, 0, SEEK_SET);
2669                       chksumtype = 0;
2670                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2671                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2672                         {
2673                           close(newfd);
2674                           fclose(fp);
2675                           continue;
2676                         }
2677                       newpkgsfps[i] = fdopen(newfd, "r");
2678                       fclose(fp);
2679                       break;
2680                     }
2681                 }
2682               dataiterator_free(&di);
2683               sat_free(matchname);
2684             }
2685           
2686           if (newpkgsfps[i])
2687             {
2688               putchar('d');
2689               fflush(stdout);
2690               continue;         /* delta worked! */
2691             }
2692           if (cinfo->type == TYPE_SUSETAGS)
2693             {
2694               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2695               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2696             }
2697           chksumtype = 0;
2698           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2699           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
2700             {
2701               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2702               exit(1);
2703             }
2704           putchar('.');
2705           fflush(stdout);
2706         }
2707       putchar('\n');
2708     }
2709
2710   if (newpkgs)
2711     {
2712       Queue conflicts;
2713
2714       printf("Searching for file conflicts\n");
2715       queue_init(&conflicts);
2716       fcstate.rpmdbstate = 0;
2717       fcstate.newpkgscnt = newpkgs;
2718       fcstate.checkq = &checkq;
2719       fcstate.newpkgsfps = newpkgsfps;
2720       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
2721       if (conflicts.count)
2722         {
2723           printf("\n");
2724           for (i = 0; i < conflicts.count; i += 5)
2725             printf("file %s of package %s conflicts with package %s\n", id2str(pool, conflicts.elements[i]), solvid2str(pool, conflicts.elements[i + 1]), solvid2str(pool, conflicts.elements[i + 3]));
2726           printf("\n");
2727           if (yesno("Re-run solver (y/n/q)? "))
2728             {
2729               for (i = 0; i < newpkgs; i++)
2730                 if (newpkgsfps[i])
2731                   fclose(newpkgsfps[i]);
2732               newpkgsfps = sat_free(newpkgsfps);
2733               solver_free(solv);
2734               pool_add_fileconflicts_deps(pool, &conflicts);
2735               pool_createwhatprovides(pool);    /* Hmm... */
2736               goto rerunsolver;
2737             }
2738         }
2739       queue_free(&conflicts);
2740     }
2741
2742   printf("Committing transaction:\n\n");
2743   transaction_order(trans, 0);
2744   for (i = 0; i < trans->steps.count; i++)
2745     {
2746       const char *evr, *evrp, *nvra;
2747       Solvable *s;
2748       int j;
2749       FILE *fp;
2750
2751       p = trans->steps.elements[i];
2752       s = pool_id2solvable(pool, p);
2753       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2754       switch(type)
2755         {
2756         case SOLVER_TRANSACTION_ERASE:
2757           printf("erase %s\n", solvid2str(pool, p));
2758           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2759             continue;
2760           /* strip epoch from evr */
2761           evr = evrp = id2str(pool, s->evr);
2762           while (*evrp >= '0' && *evrp <= '9')
2763             evrp++;
2764           if (evrp > evr && evrp[0] == ':' && evrp[1])
2765             evr = evrp + 1;
2766           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2767           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2768           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2769           break;
2770         case SOLVER_TRANSACTION_INSTALL:
2771         case SOLVER_TRANSACTION_MULTIINSTALL:
2772           printf("install %s\n", solvid2str(pool, p));
2773           for (j = 0; j < newpkgs; j++)
2774             if (checkq.elements[j] == p)
2775               break;
2776           fp = j < newpkgs ? newpkgsfps[j] : 0;
2777           if (!fp)
2778             continue;
2779           rewind(fp);
2780           lseek(fileno(fp), 0, SEEK_SET);
2781           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2782           fclose(fp);
2783           newpkgsfps[j] = 0;
2784           break;
2785         default:
2786           break;
2787         }
2788     }
2789
2790   for (i = 0; i < newpkgs; i++)
2791     if (newpkgsfps[i])
2792       fclose(newpkgsfps[i]);
2793   sat_free(newpkgsfps);
2794   queue_free(&checkq);
2795   solver_free(solv);
2796   queue_free(&job);
2797   pool_free(pool);
2798   free_repoinfos(repoinfos, nrepoinfos);
2799   sat_free(commandlinepkgs);
2800 #ifdef FEDORA
2801   yum_substitute(pool, 0);
2802 #endif
2803   exit(0);
2804 }