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