- support '-r' option a bit
[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 int
1744 limitrepo(Pool *pool, Id repofilter, Queue *job)
1745 {
1746   Queue mq;
1747   Id p, pp;
1748   int matched = 0;
1749   int i;
1750   Solvable *s;
1751
1752   queue_init(&mq);
1753   for (i = 0; i < job->count; i += 2)
1754     {
1755       queue_empty(&mq);
1756       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1757         {
1758           s = pool_id2solvable(pool, p);
1759           if (s->repo && s->repo->repoid == repofilter)
1760              queue_push(&mq, p);
1761         }
1762       if (mq.count)
1763         {
1764           if (!matched && i)
1765             {
1766               queue_deleten(job, 0, i);
1767               i = 0;
1768             }
1769           matched = 1;
1770           if (mq.count > 1)
1771             {
1772               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1773               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1774             }
1775           else
1776             {
1777               job->elements[i] = SOLVER_SOLVABLE;
1778               job->elements[i + 1] = mq.elements[0];
1779             }
1780         }
1781       else if (matched)
1782         {
1783           queue_deleten(job, i, 2);
1784           i -= 2;
1785         }
1786     }
1787   queue_free(&mq);
1788   return matched;
1789 }
1790
1791 void
1792 mkselect(Pool *pool, int mode, char *name, Queue *job)
1793 {
1794   char *r, *r2;
1795   Id archid;
1796
1797   if (*name == '/')
1798     {
1799       Dataiterator di;
1800       Queue q;
1801       int match = 0;
1802
1803       queue_init(&q);
1804       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1805       while (dataiterator_step(&di))
1806         {
1807           Solvable *s = pool->solvables + di.solvid;
1808           if (!s->repo || !pool_installable(pool, s))
1809             continue;
1810           queue_push(&q, di.solvid);
1811           dataiterator_skip_solvable(&di);
1812         }
1813       dataiterator_free(&di);
1814       if (q.count)
1815         {
1816           printf("[using file list match for '%s']\n", name);
1817           match = 1;
1818           if (q.count > 1)
1819             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1820           else
1821             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1822         }
1823       queue_free(&q);
1824       if (match)
1825         return;
1826     }
1827   if ((r = strpbrk(name, "<=>")) != 0)
1828     {
1829       /* relation case, support:
1830        * depglob rel
1831        * depglob.rpm rel
1832        */
1833       int rflags = 0;
1834       int nend = r - name;
1835       for (; *r; r++)
1836         {
1837           if (*r == '<')
1838             rflags |= REL_LT;
1839           else if (*r == '=')
1840             rflags |= REL_EQ;
1841           else if (*r == '>')
1842             rflags |= REL_GT;
1843           else
1844             break;
1845         }
1846       while (*r && *r == ' ' && *r == '\t')
1847         r++;
1848       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1849         nend--;
1850       name[nend] = 0;
1851       if (!*name || !*r)
1852         {
1853           fprintf(stderr, "bad relation\n");
1854           exit(1);
1855         }
1856       if (depglob(pool, name, job))
1857         {
1858           addrelation(pool, job, rflags, str2id(pool, r, 1));
1859           return;
1860         }
1861       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1862         {
1863           *r2 = 0;
1864           if (depglob(pool, name, job))
1865             {
1866               *r2 = '.';
1867               addrelation(pool, job, REL_ARCH, archid);
1868               addrelation(pool, job, rflags, str2id(pool, r, 1));
1869               return;
1870             }
1871           *r2 = '.';
1872         }
1873     }
1874   else
1875     {
1876       /* no relation case, support:
1877        * depglob
1878        * depglob.arch
1879        * depglob-version-release
1880        * depglob-version-release.arch
1881        */
1882       if (depglob(pool, name, job))
1883         return;
1884       archid = 0;
1885       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1886         {
1887           *r = 0;
1888           if (depglob(pool, name, job))
1889             {
1890               *r = '.';
1891               addrelation(pool, job, REL_ARCH, archid);
1892               return;
1893             }
1894           *r = '.';
1895         }
1896       if ((r = strrchr(name, '-')) != 0)
1897         {
1898           *r = 0;
1899           if (depglob(pool, name, job))
1900             {
1901               /* have just the version */
1902               *r = '-';
1903               if (limitevr(pool, r + 1, job, 0))
1904                 return;
1905             }
1906           if ((r2 = strrchr(name, '-')) != 0)
1907             {
1908               *r = '-';
1909               *r2 = 0;
1910               if (depglob(pool, name, job))
1911                 {
1912                   *r2 = '-';
1913                   if (limitevr(pool, r2 + 1, job, 0))
1914                     return;
1915                 }
1916               *r2 = '-';
1917             }
1918           *r = '-';
1919         }
1920     }
1921   fprintf(stderr, "nothing matches '%s'\n", name);
1922   exit(1);
1923 }
1924
1925
1926 int
1927 yesno(const char *str)
1928 {
1929   char inbuf[128], *ip;
1930
1931   for (;;)
1932     {
1933       printf("%s", str);
1934       fflush(stdout);
1935       *inbuf = 0;
1936       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1937         {
1938           printf("Abort.\n");
1939           exit(1);
1940         }
1941       while (*ip == ' ' || *ip == '\t')
1942         ip++;
1943       if (*ip == 'q')
1944         {
1945           printf("Abort.\n");
1946           exit(1);
1947         }
1948       if (*ip == 'y' || *ip == 'n')
1949         return *ip == 'y' ? 1 : 0;
1950     }
1951 }
1952
1953 struct fcstate {
1954   FILE **newpkgsfps;
1955   Queue *checkq;
1956   int newpkgscnt;
1957   void *rpmdbstate;
1958 };
1959
1960 static void *
1961 fileconflict_cb(Pool *pool, Id p, void *cbdata)
1962 {
1963   struct fcstate *fcstate = cbdata;
1964   Solvable *s;
1965   Id rpmdbid;
1966   int i;
1967   FILE *fp;
1968
1969   if (!p)
1970     {
1971       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1972       return 0;
1973     }
1974   s = pool_id2solvable(pool, p);
1975   if (pool->installed && s->repo == pool->installed)
1976     {
1977       if (!s->repo->rpmdbid)
1978         return 0;
1979       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1980       if (!rpmdbid)
1981         return 0;
1982        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1983     }
1984   for (i = 0; i < fcstate->newpkgscnt; i++)
1985     if (fcstate->checkq->elements[i] == p)
1986       break;
1987   if (i == fcstate->newpkgscnt)
1988     return 0;
1989   fp = fcstate->newpkgsfps[i];
1990   if (!fp)
1991     return 0;
1992   rewind(fp);
1993   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1994 }
1995
1996 void
1997 runrpm(const char *arg, const char *name, int dupfd3)
1998 {
1999   pid_t pid;
2000   int status;
2001
2002   if ((pid = fork()) == (pid_t)-1)
2003     {
2004       perror("fork");
2005       exit(1);
2006     }
2007   if (pid == 0)
2008     {
2009       if (dupfd3 != -1 && dupfd3 != 3)
2010         {
2011           dup2(dupfd3, 3);
2012           close(dupfd3);
2013         }
2014       if (dupfd3 != -1)
2015         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2016       if (strcmp(arg, "-e") == 0)
2017         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2018       else
2019         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2020       perror("rpm");
2021       _exit(0);
2022     }
2023   while (waitpid(pid, &status, 0) != pid)
2024     ;
2025   if (status)
2026     {
2027       printf("rpm failed\n");
2028       exit(1);
2029     }
2030 }
2031
2032 static Id
2033 nscallback(Pool *pool, void *data, Id name, Id evr)
2034 {
2035   if (name == NAMESPACE_PRODUCTBUDDY)
2036     {    
2037       /* SUSE specific hack: each product has an associated rpm */
2038       Solvable *s = pool->solvables + evr; 
2039       Id p, pp, cap; 
2040       
2041       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
2042       if (!cap)
2043         return 0;
2044       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
2045       if (!cap)
2046         return 0;
2047       FOR_PROVIDES(p, pp, cap) 
2048         {
2049           Solvable *ps = pool->solvables + p; 
2050           if (ps->repo == s->repo && ps->arch == s->arch)
2051             break;
2052         }
2053       return p;
2054     }
2055   return 0;
2056 }
2057
2058 #ifdef SOFTLOCKS_PATH
2059
2060 void
2061 addsoftlocks(Pool *pool, Queue *job)
2062 {
2063   FILE *fp;
2064   Id type, id, p, pp;
2065   char *bp, *ep, buf[4096];
2066
2067   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2068     return;
2069   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2070     {
2071       while (*bp == ' ' || *bp == '\t')
2072         bp++;
2073       if (!*bp || *bp == '#')
2074         continue;
2075       for (ep = bp; *ep; ep++)
2076         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2077           break;
2078       *ep = 0;
2079       type = SOLVER_SOLVABLE_NAME;
2080       if (!strncmp(bp, "provides:", 9) && bp[9])
2081         {
2082           type = SOLVER_SOLVABLE_PROVIDES;
2083           bp += 9;
2084         }
2085       id = str2id(pool, bp, 1);
2086       if (pool->installed)
2087         {
2088           FOR_JOB_SELECT(p, pp, type, id)
2089             if (pool->solvables[p].repo == pool->installed)
2090               break;
2091           if (p)
2092             continue;   /* ignore, as it is already installed */
2093         }
2094       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2095     }
2096   fclose(fp);
2097 }
2098
2099 #endif
2100
2101
2102 void
2103 rewrite_repos(Pool *pool, Id *addedfileprovides)
2104 {
2105   Repo *repo;
2106   Repodata *data;
2107   Map providedids;
2108   Queue fileprovidesq;
2109   Id id;
2110   int i, j, n, nprovidedids;
2111   struct repoinfo *cinfo;
2112
2113   map_init(&providedids, pool->ss.nstrings);
2114   queue_init(&fileprovidesq);
2115   for (nprovidedids = 0; (id = addedfileprovides[nprovidedids]) != 0; nprovidedids++)
2116     MAPSET(&providedids, id);
2117   FOR_REPOS(i, repo)
2118     {
2119       /* make sure this repo has just one main repodata */
2120       if (!repo->nrepodata)
2121         continue;
2122       cinfo = repo->appdata;
2123       data = repo->repodata + 0;
2124       if (data->store.pagefd == -1)
2125         continue;
2126       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2127         {
2128           n = 0;
2129           for (j = 0; j < fileprovidesq.count; j++)
2130             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2131               n++;
2132           if (n == nprovidedids)
2133             continue;   /* nothing new added */
2134         }
2135       /* oh my! */
2136       for (j = 0; addedfileprovides[j]; j++)
2137         repodata_add_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[j]);
2138       repodata_internalize(data);
2139       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2140     }
2141   queue_free(&fileprovidesq);
2142   map_free(&providedids);
2143 }
2144
2145 #define MODE_LIST        0
2146 #define MODE_INSTALL     1
2147 #define MODE_ERASE       2
2148 #define MODE_UPDATE      3
2149 #define MODE_DISTUPGRADE 4
2150 #define MODE_VERIFY      5
2151 #define MODE_PATCH       6
2152 #define MODE_INFO        7
2153 #define MODE_REPOLIST    8
2154 #define MODE_SEARCH      9
2155
2156 void
2157 usage(int r)
2158 {
2159   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2160   fprintf(stderr, "\n");
2161   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2162   fprintf(stderr, "                  versions from the repositories\n");
2163   fprintf(stderr, "    erase:        erase installed packages\n");
2164   fprintf(stderr, "    info:         display package information\n");
2165   fprintf(stderr, "    install:      install packages\n");
2166   fprintf(stderr, "    list:         list packages\n");
2167   fprintf(stderr, "    repos:        list enabled repositories\n");
2168   fprintf(stderr, "    search:       search name/summary/description\n");
2169   fprintf(stderr, "    update:       update installed packages\n");
2170   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2171   fprintf(stderr, "\n");
2172   exit(r);
2173 }
2174
2175 int
2176 main(int argc, char **argv)
2177 {
2178   Pool *pool;
2179   Repo *commandlinerepo = 0;
2180   Id *commandlinepkgs = 0;
2181   Id p, pp;
2182   struct repoinfo *repoinfos;
2183   int nrepoinfos = 0;
2184   int mainmode = 0, mode = 0;
2185   int i, newpkgs;
2186   Queue job, checkq;
2187   Solver *solv = 0;
2188   Transaction *trans;
2189   char inbuf[128], *ip;
2190   int allpkgs = 0;
2191   FILE **newpkgsfps;
2192   struct fcstate fcstate;
2193   Id *addedfileprovides = 0;
2194   Id repofilter = 0;
2195
2196   argc--;
2197   argv++;
2198   if (!argv[0])
2199     usage(1);
2200   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2201     {
2202       mainmode = MODE_INSTALL;
2203       mode = SOLVER_INSTALL;
2204     }
2205   else if (!strcmp(argv[0], "patch"))
2206     {
2207       mainmode = MODE_PATCH;
2208       mode = SOLVER_INSTALL;
2209     }
2210   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2211     {
2212       mainmode = MODE_ERASE;
2213       mode = SOLVER_ERASE;
2214     }
2215   else if (!strcmp(argv[0], "list"))
2216     {
2217       mainmode = MODE_LIST;
2218       mode = 0;
2219     }
2220   else if (!strcmp(argv[0], "info"))
2221     {
2222       mainmode = MODE_INFO;
2223       mode = 0;
2224     }
2225   else if (!strcmp(argv[0], "search"))
2226     {
2227       mainmode = MODE_SEARCH;
2228       mode = 0;
2229     }
2230   else if (!strcmp(argv[0], "verify"))
2231     {
2232       mainmode = MODE_VERIFY;
2233       mode = SOLVER_VERIFY;
2234     }
2235   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2236     {
2237       mainmode = MODE_UPDATE;
2238       mode = SOLVER_UPDATE;
2239     }
2240   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2241     {
2242       mainmode = MODE_DISTUPGRADE;
2243       mode = SOLVER_UPDATE;
2244     }
2245   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2246     {
2247       mainmode = MODE_REPOLIST;
2248       mode = 0;
2249     }
2250   else
2251     usage(1);
2252
2253   pool = pool_create();
2254 #ifdef FEDORA
2255   pool->obsoleteusescolors = 1;
2256 #endif
2257   pool_setloadcallback(pool, load_stub, 0);
2258   pool->nscallback = nscallback;
2259   // pool_setdebuglevel(pool, 2);
2260   setarch(pool);
2261   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
2262
2263   if (mainmode == MODE_REPOLIST)
2264     {
2265       int j = 1;
2266       for (i = 0; i < nrepoinfos; i++)
2267         {
2268           struct repoinfo *cinfo = repoinfos + i;
2269           if (!cinfo->enabled)
2270             continue;
2271           printf("%d: %-20s %s\n", j++, cinfo->alias, cinfo->name);
2272         }
2273       exit(0);
2274     }
2275
2276   read_repos(pool, repoinfos, nrepoinfos);
2277
2278   if (argc > 2 && !strcmp(argv[1], "-r"))
2279     {
2280       const char *rname = argv[2], *rp;
2281       for (rp = rname; *rp; rp++)
2282         if (*rp <= '0' || *rp >= '9')
2283           break;
2284       if (!*rp)
2285         {
2286           /* repo specified by number */
2287           int rnum = atoi(rname);
2288           for (i = 0; i < nrepoinfos; i++)
2289             {
2290               struct repoinfo *cinfo = repoinfos + i;
2291               if (!cinfo->enabled)
2292                 continue;
2293               if (--rnum == 0)
2294                 repofilter = cinfo->repo->repoid;
2295             }
2296         }
2297       else
2298         {
2299           /* repo specified by alias */
2300           Repo *repo;
2301           FOR_REPOS(i, repo)
2302             {
2303               if (!strcasecmp(rname, repo->name))
2304                 repofilter = repo->repoid;
2305             }
2306         }
2307       if (!repofilter)
2308         {
2309           fprintf(stderr, "%s: no such repo\n", rname);
2310           exit(1);
2311         }
2312       argc -= 2;
2313       argv += 2;
2314     }
2315   if (mainmode == MODE_SEARCH)
2316     {
2317       Dataiterator di;
2318       Map m;
2319       if (argc != 2)
2320         usage(1);
2321       map_init(&m, pool->nsolvables);
2322       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2323       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2324       dataiterator_set_search(&di, 0, 0);
2325       while (dataiterator_step(&di))
2326         MAPSET(&m, di.solvid);
2327       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2328       dataiterator_set_search(&di, 0, 0);
2329       while (dataiterator_step(&di))
2330         MAPSET(&m, di.solvid);
2331       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2332       dataiterator_set_search(&di, 0, 0);
2333       while (dataiterator_step(&di))
2334         MAPSET(&m, di.solvid);
2335       dataiterator_free(&di);
2336
2337       for (p = 1; p < pool->nsolvables; p++)
2338         {
2339           Solvable *s = pool_id2solvable(pool, p);
2340           if (!MAPTST(&m, p))
2341             continue;
2342           printf("  - %s: %s\n", solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2343         }
2344       map_free(&m);
2345       exit(0);
2346     }
2347
2348
2349   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2350     {
2351       for (i = 1; i < argc; i++)
2352         {
2353           int l;
2354           l = strlen(argv[i]);
2355           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2356             continue;
2357           if (access(argv[i], R_OK))
2358             {
2359               perror(argv[i]);
2360               exit(1);
2361             }
2362           if (!commandlinepkgs)
2363             commandlinepkgs = sat_calloc(argc, sizeof(Id));
2364           if (!commandlinerepo)
2365             commandlinerepo = repo_create(pool, "@commandline");
2366           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2367           commandlinepkgs[i] = commandlinerepo->end - 1;
2368         }
2369       if (commandlinerepo)
2370         repo_internalize(commandlinerepo);
2371     }
2372
2373   // FOR_REPOS(i, repo)
2374   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2375   addedfileprovides = 0;
2376   pool_addfileprovides_ids(pool, 0, &addedfileprovides);
2377   if (addedfileprovides && *addedfileprovides)
2378     rewrite_repos(pool, addedfileprovides);
2379   sat_free(addedfileprovides);
2380   pool_createwhatprovides(pool);
2381
2382   queue_init(&job);
2383   for (i = 1; i < argc; i++)
2384     {
2385       Queue job2;
2386       int j;
2387
2388       if (commandlinepkgs && commandlinepkgs[i])
2389         {
2390           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2391           continue;
2392         }
2393       queue_init(&job2);
2394       mkselect(pool, mode, argv[i], &job2);
2395       if (repofilter && !limitrepo(pool, repofilter, &job2))
2396         {
2397           fprintf(stderr, "nothing in repo matches '%s'\n", argv[i]);
2398           exit(1);
2399         }
2400       for (j = 0; j < job2.count; j++)
2401         queue_push(&job, job2.elements[j]);
2402       queue_free(&job2);
2403     }
2404
2405   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2406     {
2407       printf("no package matched\n");
2408       exit(1);
2409     }
2410
2411   if (!job.count && !repofilter)
2412     allpkgs = 1;
2413
2414   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2415     {
2416       /* list mode, no solver needed */
2417       for (i = 0; i < job.count; i += 2)
2418         {
2419           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2420             {
2421               Solvable *s = pool_id2solvable(pool, p);
2422               if (mainmode == MODE_INFO)
2423                 {
2424                   printf("Name:        %s\n", solvable2str(pool, s));
2425                   printf("Repo:        %s\n", s->repo->name);
2426                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2427                   printf("Url:         %s\n", solvable_lookup_str(s, SOLVABLE_URL));
2428                   printf("License:     %s\n", solvable_lookup_str(s, SOLVABLE_LICENSE));
2429                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2430                   printf("\n");
2431                 }
2432               else
2433                 {
2434                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
2435                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2436                   if (sum)
2437                     printf("    %s\n", sum);
2438                 }
2439             }
2440         }
2441       queue_free(&job);
2442       pool_free(pool);
2443       free_repoinfos(repoinfos, nrepoinfos);
2444       sat_free(commandlinepkgs);
2445 #ifdef FEDORA
2446       yum_substitute(pool, 0);
2447 #endif
2448       exit(0);
2449     }
2450
2451   if (mainmode == MODE_PATCH)
2452     {
2453       int pruneyou = 0;
2454       Map installedmap;
2455       Solvable *s;
2456
2457       map_init(&installedmap, pool->nsolvables);
2458       if (pool->installed)
2459         FOR_REPO_SOLVABLES(pool->installed, p, s)
2460           MAPSET(&installedmap, p);
2461
2462       /* install all patches */
2463       for (p = 1; p < pool->nsolvables; p++)
2464         {
2465           const char *type;
2466           int r;
2467           Id p2;
2468
2469           s = pool->solvables + p;
2470           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2471             continue;
2472           FOR_PROVIDES(p2, pp, s->name)
2473             {
2474               Solvable *s2 = pool->solvables + p2;
2475               if (s2->name != s->name)
2476                 continue;
2477               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2478               if (r < 0 || (r == 0 && p > p2))
2479                 break;
2480             }
2481           if (p2)
2482             continue;
2483           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2484           if (type && !strcmp(type, "optional"))
2485             continue;
2486           r = solvable_trivial_installable_map(s, &installedmap, 0);
2487           if (r == -1)
2488             continue;
2489           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2490             {
2491               if (!pruneyou++)
2492                 queue_empty(&job);
2493             }
2494           else if (pruneyou)
2495             continue;
2496           queue_push2(&job, SOLVER_SOLVABLE, p);
2497         }
2498       map_free(&installedmap);
2499     }
2500
2501   // add mode
2502   for (i = 0; i < job.count; i += 2)
2503     {
2504       if (mode == SOLVER_UPDATE)
2505         {
2506           /* make update of not installed packages an install */
2507           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2508             if (pool->installed && pool->solvables[p].repo == pool->installed)
2509               break;
2510           if (!p)
2511             {
2512               job.elements[i] |= SOLVER_INSTALL;
2513               continue;
2514             }
2515         }
2516       job.elements[i] |= mode;
2517     }
2518
2519   // multiversion test
2520   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2521   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2522   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2523
2524 #ifdef SOFTLOCKS_PATH
2525   addsoftlocks(pool, &job);
2526 #endif
2527
2528 rerunsolver:
2529   for (;;)
2530     {
2531       Id problem, solution;
2532       int pcnt, scnt;
2533
2534       solv = solver_create(pool);
2535       solv->ignorealreadyrecommended = 1;
2536       solv->updatesystem = allpkgs && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
2537       solv->dosplitprovides = solv->updatesystem;
2538       solv->fixsystem = allpkgs && (mainmode == MODE_VERIFY);
2539       if (mainmode == MODE_DISTUPGRADE && allpkgs)
2540         {
2541           solv->distupgrade = 1;
2542           solv->allowdowngrade = 1;
2543           solv->allowarchchange = 1;
2544           solv->allowvendorchange = 1;
2545         }
2546       if (mainmode == MODE_ERASE)
2547         solv->allowuninstall = 1;       /* don't nag */
2548
2549       if (mainmode == MODE_DISTUPGRADE && repofilter)
2550         queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter);
2551       solver_solve(solv, &job);
2552       if (!solv->problems.count)
2553         break;
2554       pcnt = solver_problem_count(solv);
2555       printf("Found %d problems:\n", pcnt);
2556       for (problem = 1; problem <= pcnt; problem++)
2557         {
2558           int take = 0;
2559           printf("Problem %d:\n", problem);
2560           solver_printprobleminfo(solv, problem);
2561           printf("\n");
2562           scnt = solver_solution_count(solv, problem);
2563           for (solution = 1; solution <= scnt; solution++)
2564             {
2565               printf("Solution %d:\n", solution);
2566               solver_printsolution(solv, problem, solution);
2567               printf("\n");
2568             }
2569           for (;;)
2570             {
2571               printf("Please choose a solution: ");
2572               fflush(stdout);
2573               *inbuf = 0;
2574               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2575                 {
2576                   printf("Abort.\n");
2577                   exit(1);
2578                 }
2579               while (*ip == ' ' || *ip == '\t')
2580                 ip++;
2581               if (*ip >= '0' && *ip <= '9')
2582                 {
2583                   take = atoi(ip);
2584                   if (take >= 1 && take <= scnt)
2585                     break;
2586                 }
2587               if (*ip == 's')
2588                 {
2589                   take = 0;
2590                   break;
2591                 }
2592               if (*ip == 'q')
2593                 {
2594                   printf("Abort.\n");
2595                   exit(1);
2596                 }
2597             }
2598           if (!take)
2599             continue;
2600           solver_take_solution(solv, problem, take, &job);
2601         }
2602       solver_free(solv);
2603       solv = 0;
2604     }
2605
2606   trans = &solv->trans;
2607   if (!trans->steps.count)
2608     {
2609       printf("Nothing to do.\n");
2610       exit(1);
2611     }
2612   printf("\n");
2613   printf("Transaction summary:\n\n");
2614   solver_printtransaction(solv);
2615
2616 #ifndef FEDORA
2617   if (1)
2618     {
2619       DUChanges duc[4];
2620       int i;
2621
2622       duc[0].path = "/";
2623       duc[1].path = "/usr/share/man";
2624       duc[2].path = "/sbin";
2625       duc[3].path = "/etc";
2626       transaction_calc_duchanges(trans, duc, 4);
2627       for (i = 0; i < 4; i++)
2628         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2629     }
2630 #endif
2631   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
2632   printf("\n");
2633
2634   if (!yesno("OK to continue (y/n)? "))
2635     {
2636       printf("Abort.\n");
2637       exit(1);
2638     }
2639
2640   queue_init(&checkq);
2641   newpkgs = transaction_installedresult(trans, &checkq);
2642   newpkgsfps = 0;
2643
2644   if (newpkgs)
2645     {
2646       int downloadsize = 0;
2647       for (i = 0; i < newpkgs; i++)
2648         {
2649           Solvable *s;
2650
2651           p = checkq.elements[i];
2652           s = pool_id2solvable(pool, p);
2653           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
2654         }
2655       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
2656       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
2657       for (i = 0; i < newpkgs; i++)
2658         {
2659           unsigned int medianr;
2660           char *loc;
2661           Solvable *s;
2662           struct repoinfo *cinfo;
2663           const unsigned char *chksum;
2664           Id chksumtype;
2665           Dataiterator di;
2666
2667           p = checkq.elements[i];
2668           s = pool_id2solvable(pool, p);
2669           if (s->repo == commandlinerepo)
2670             {
2671               loc = solvable_get_location(s, &medianr);
2672               if (!(newpkgsfps[i] = fopen(loc, "r")))
2673                 {
2674                   perror(loc);
2675                   exit(1);
2676                 }
2677               putchar('.');
2678               continue;
2679             }
2680           cinfo = s->repo->appdata;
2681           if (!cinfo)
2682             {
2683               printf("%s: no repository information\n", s->repo->name);
2684               exit(1);
2685             }
2686           loc = solvable_get_location(s, &medianr);
2687           if (!loc)
2688              continue;
2689
2690           if (pool->installed && pool->installed->nsolvables)
2691             {
2692               /* try a delta first */
2693               char *matchname = strdup(id2str(pool, s->name));
2694               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2695               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2696               while (dataiterator_step(&di))
2697                 {
2698                   Id baseevr, op;
2699
2700                   dataiterator_setpos_parent(&di);
2701                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2702                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2703                     continue;
2704                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2705                   FOR_PROVIDES(op, pp, s->name)
2706                     {
2707                       Solvable *os = pool->solvables + op;
2708                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2709                         break;
2710                     }
2711                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2712                     {
2713                       /* base is installed, run sequence check */
2714                       const char *seqname;
2715                       const char *seqevr;
2716                       const char *seqnum;
2717                       const char *seq;
2718                       const char *dloc;
2719                       FILE *fp;
2720                       char cmd[128];
2721                       int newfd;
2722
2723                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2724                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2725                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2726                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2727                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2728 #ifdef FEDORA
2729                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
2730 #else
2731                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
2732 #endif
2733                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
2734                         continue;       /* didn't match */
2735                       /* looks good, download delta */
2736                       chksumtype = 0;
2737                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2738                       if (!chksumtype)
2739                         continue;       /* no way! */
2740                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2741                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2742                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2743                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2744                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2745                         continue;
2746                       /* got it, now reconstruct */
2747                       newfd = opentmpfile();
2748 #ifdef FEDORA
2749                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
2750 #else
2751                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2752 #endif
2753                       fcntl(fileno(fp), F_SETFD, 0);
2754                       if (system(cmd))
2755                         {
2756                           close(newfd);
2757                           fclose(fp);
2758                           continue;
2759                         }
2760                       lseek(newfd, 0, SEEK_SET);
2761                       chksumtype = 0;
2762                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2763                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2764                         {
2765                           close(newfd);
2766                           fclose(fp);
2767                           continue;
2768                         }
2769                       newpkgsfps[i] = fdopen(newfd, "r");
2770                       fclose(fp);
2771                       break;
2772                     }
2773                 }
2774               dataiterator_free(&di);
2775               sat_free(matchname);
2776             }
2777           
2778           if (newpkgsfps[i])
2779             {
2780               putchar('d');
2781               fflush(stdout);
2782               continue;         /* delta worked! */
2783             }
2784           if (cinfo->type == TYPE_SUSETAGS)
2785             {
2786               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2787               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2788             }
2789           chksumtype = 0;
2790           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2791           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
2792             {
2793               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2794               exit(1);
2795             }
2796           putchar('.');
2797           fflush(stdout);
2798         }
2799       putchar('\n');
2800     }
2801
2802   if (newpkgs)
2803     {
2804       Queue conflicts;
2805
2806       printf("Searching for file conflicts\n");
2807       queue_init(&conflicts);
2808       fcstate.rpmdbstate = 0;
2809       fcstate.newpkgscnt = newpkgs;
2810       fcstate.checkq = &checkq;
2811       fcstate.newpkgsfps = newpkgsfps;
2812       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
2813       if (conflicts.count)
2814         {
2815           printf("\n");
2816           for (i = 0; i < conflicts.count; i += 5)
2817             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]));
2818           printf("\n");
2819           if (yesno("Re-run solver (y/n/q)? "))
2820             {
2821               for (i = 0; i < newpkgs; i++)
2822                 if (newpkgsfps[i])
2823                   fclose(newpkgsfps[i]);
2824               newpkgsfps = sat_free(newpkgsfps);
2825               solver_free(solv);
2826               pool_add_fileconflicts_deps(pool, &conflicts);
2827               pool_createwhatprovides(pool);    /* Hmm... */
2828               goto rerunsolver;
2829             }
2830         }
2831       queue_free(&conflicts);
2832     }
2833
2834   printf("Committing transaction:\n\n");
2835   transaction_order(trans, 0);
2836   for (i = 0; i < trans->steps.count; i++)
2837     {
2838       const char *evr, *evrp, *nvra;
2839       Solvable *s;
2840       int j;
2841       FILE *fp;
2842
2843       p = trans->steps.elements[i];
2844       s = pool_id2solvable(pool, p);
2845       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2846       switch(type)
2847         {
2848         case SOLVER_TRANSACTION_ERASE:
2849           printf("erase %s\n", solvid2str(pool, p));
2850           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2851             continue;
2852           /* strip epoch from evr */
2853           evr = evrp = id2str(pool, s->evr);
2854           while (*evrp >= '0' && *evrp <= '9')
2855             evrp++;
2856           if (evrp > evr && evrp[0] == ':' && evrp[1])
2857             evr = evrp + 1;
2858           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2859           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2860           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2861           break;
2862         case SOLVER_TRANSACTION_INSTALL:
2863         case SOLVER_TRANSACTION_MULTIINSTALL:
2864           printf("install %s\n", solvid2str(pool, p));
2865           for (j = 0; j < newpkgs; j++)
2866             if (checkq.elements[j] == p)
2867               break;
2868           fp = j < newpkgs ? newpkgsfps[j] : 0;
2869           if (!fp)
2870             continue;
2871           rewind(fp);
2872           lseek(fileno(fp), 0, SEEK_SET);
2873           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2874           fclose(fp);
2875           newpkgsfps[j] = 0;
2876           break;
2877         default:
2878           break;
2879         }
2880     }
2881
2882   for (i = 0; i < newpkgs; i++)
2883     if (newpkgsfps[i])
2884       fclose(newpkgsfps[i]);
2885   sat_free(newpkgsfps);
2886   queue_free(&checkq);
2887   solver_free(solv);
2888   queue_free(&job);
2889   pool_free(pool);
2890   free_repoinfos(repoinfos, nrepoinfos);
2891   sat_free(commandlinepkgs);
2892 #ifdef FEDORA
2893   yum_substitute(pool, 0);
2894 #endif
2895   exit(0);
2896 }