- set default prio to 99
[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 (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1212         continue;
1213       ext[0] = di.kv.str[9];
1214       ext[1] = di.kv.str[10];
1215       ext[2] = 0;
1216       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1217         continue;
1218       handle = repodata_new_handle(data);
1219       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1220       if (filechksumtype)
1221         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1222       if (!strcmp(ext, "DU"))
1223         {
1224           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1225           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1226         }
1227       if (!strcmp(ext, "FL"))
1228         {
1229           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1230           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1231         }
1232       else
1233         {
1234           for (i = 0; susetags_langtags[i]; i += 2)
1235             {
1236               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1237               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1238             }
1239         }
1240       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1241     }
1242   dataiterator_free(&di);
1243 }
1244
1245
1246 static inline int
1247 iscompressed(const char *name)
1248 {
1249   int l = strlen(name);
1250   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
1251 }
1252
1253
1254 /* load callback */
1255
1256 int
1257 load_stub(Pool *pool, Repodata *data, void *dp)
1258 {
1259   const char *filename, *descrdir, *repomdtype;
1260   const unsigned char *filechksum;
1261   Id filechksumtype;
1262   struct repoinfo *cinfo;
1263   FILE *fp;
1264   Id defvendor;
1265   char ext[3];
1266
1267   cinfo = data->repo->appdata;
1268
1269   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1270   if (filename)
1271     {
1272       /* susetags load */
1273       ext[0] = filename[9];
1274       ext[1] = filename[10];
1275       ext[2] = 0;
1276 #if 1
1277       printf("[%s:%s", data->repo->name, ext);
1278 #endif
1279       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1280         {
1281           printf(" cached]\n"); fflush(stdout);
1282           return 1;
1283         }
1284 #if 1
1285       printf(" fetching]\n"); fflush(stdout);
1286 #endif
1287       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1288       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
1289       if (!descrdir)
1290         descrdir = "suse/setup/descr";
1291       filechksumtype = 0;
1292       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1293       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1294         return 0;
1295       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1296       fclose(fp);
1297       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1298       return 1;
1299     }
1300
1301   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1302   if (repomdtype)
1303     {
1304       if (!strcmp(repomdtype, "filelists"))
1305         strcpy(ext, "FL");
1306       else if (!strcmp(repomdtype, "deltainfo"))
1307         strcpy(ext, "DL");
1308       else
1309         return 0;
1310 #if 1
1311       printf("[%s:%s", data->repo->name, ext);
1312 #endif
1313       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1314         {
1315           printf(" cached]\n");fflush(stdout);
1316           return 1;
1317         }
1318       printf(" fetching]\n"); fflush(stdout);
1319       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1320       filechksumtype = 0;
1321       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1322       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1323         return 0;
1324       if (!strcmp(ext, "FL"))
1325         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1326       else if (!strcmp(ext, "DL"))
1327         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1328       fclose(fp);
1329       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1330       return 1;
1331     }
1332
1333   return 0;
1334 }
1335
1336 static unsigned char installedcookie[32];
1337
1338 void
1339 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1340 {
1341   Repo *repo;
1342   struct repoinfo *cinfo;
1343   int i;
1344   FILE *fp;
1345   FILE *sigfp;
1346   const char *filename;
1347   const unsigned char *filechksum;
1348   Id filechksumtype;
1349   const char *descrdir;
1350   int defvendor;
1351   struct stat stb;
1352   Pool *sigpool = 0;
1353   Repodata *data;
1354   int badchecksum;
1355   int dorefresh;
1356
1357   repo = repo_create(pool, "@System");
1358   printf("rpm database:");
1359   if (stat("/var/lib/rpm/Packages", &stb))
1360     memset(&stb, 0, sizeof(&stb));
1361   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, installedcookie);
1362   if (usecachedrepo(repo, 0, installedcookie, 0))
1363     printf(" cached\n");
1364   else
1365     {
1366       FILE *ofp;
1367       printf(" reading\n");
1368       int done = 0;
1369
1370 #ifdef PRODUCTS_PATH
1371       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1372 #endif
1373       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1374         {
1375           Repo *ref = repo_create(pool, "@System.old");
1376           if (!repo_add_solv(ref, ofp))
1377             {
1378               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1379               done = 1;
1380             }
1381           fclose(ofp);
1382           repo_free(ref, 1);
1383         }
1384       if (!done)
1385         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1386       writecachedrepo(repo, 0, 0, installedcookie);
1387     }
1388   pool_set_installed(pool, repo);
1389
1390   for (i = 0; i < nrepoinfos; i++)
1391     {
1392       cinfo = repoinfos + i;
1393       if (!cinfo->enabled)
1394         continue;
1395
1396       repo = repo_create(pool, cinfo->alias);
1397       cinfo->repo = repo;
1398       repo->appdata = cinfo;
1399       repo->priority = 99 - cinfo->priority;
1400
1401       dorefresh = cinfo->autorefresh;
1402       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1403         {
1404           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1405             dorefresh = 0;
1406         }
1407       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1408         {
1409           printf("repo '%s':", cinfo->alias);
1410           printf(" cached\n");
1411           continue;
1412         }
1413       badchecksum = 0;
1414       switch (cinfo->type)
1415         {
1416         case TYPE_RPMMD:
1417           printf("rpmmd repo '%s':", cinfo->alias);
1418           fflush(stdout);
1419           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1420             {
1421               printf(" no repomd.xml file, skipped\n");
1422               repo_free(repo, 1);
1423               cinfo->repo = 0;
1424               break;
1425             }
1426           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1427           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1428             {
1429               printf(" cached\n");
1430               fclose(fp);
1431               break;
1432             }
1433           if (cinfo->repo_gpgcheck)
1434             {
1435               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1436               if (!sigfp)
1437                 {
1438                   printf(" unsigned, skipped\n");
1439                   fclose(fp);
1440                   break;
1441                 }
1442               if (!sigpool)
1443                 sigpool = read_sigs();
1444               if (!checksig(sigpool, fp, sigfp))
1445                 {
1446                   printf(" checksig failed, skipped\n");
1447                   fclose(sigfp);
1448                   fclose(fp);
1449                   break;
1450                 }
1451               fclose(sigfp);
1452             }
1453           repo_add_repomdxml(repo, fp, 0);
1454           fclose(fp);
1455           printf(" fetching\n");
1456           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1457           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1458             {
1459               repo_add_rpmmd(repo, fp, 0, 0);
1460               fclose(fp);
1461             }
1462           if (badchecksum)
1463             break;      /* hopeless */
1464
1465           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1466           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1467             {
1468               repo_add_updateinfoxml(repo, fp, 0);
1469               fclose(fp);
1470             }
1471
1472           data = repo_add_repodata(repo, 0);
1473           if (!repomd_add_ext(repo, data, "deltainfo"))
1474             repomd_add_ext(repo, data, "prestodelta");
1475           repomd_add_ext(repo, data, "filelists");
1476           repodata_internalize(data);
1477           if (!badchecksum)
1478             writecachedrepo(repo, 0, 0, cinfo->cookie);
1479           repodata_create_stubs(repo_last_repodata(repo));
1480           break;
1481
1482         case TYPE_SUSETAGS:
1483           printf("susetags repo '%s':", cinfo->alias);
1484           fflush(stdout);
1485           descrdir = 0;
1486           defvendor = 0;
1487           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1488             {
1489               printf(" no content file, skipped\n");
1490               repo_free(repo, 1);
1491               cinfo->repo = 0;
1492               break;
1493             }
1494           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1495           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1496             {
1497               printf(" cached\n");
1498               fclose(fp);
1499               break;
1500             }
1501           if (cinfo->repo_gpgcheck)
1502             {
1503               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1504               if (!sigfp)
1505                 {
1506                   printf(" unsigned, skipped\n");
1507                   fclose(fp);
1508                   break;
1509                 }
1510               if (sigfp)
1511                 {
1512                   if (!sigpool)
1513                     sigpool = read_sigs();
1514                   if (!checksig(sigpool, fp, sigfp))
1515                     {
1516                       printf(" checksig failed, skipped\n");
1517                       fclose(sigfp);
1518                       fclose(fp);
1519                       break;
1520                     }
1521                   fclose(sigfp);
1522                 }
1523             }
1524           repo_add_content(repo, fp, 0);
1525           fclose(fp);
1526           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1527           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1528           if (!descrdir)
1529             descrdir = "suse/setup/descr";
1530           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1531           if (!filename)
1532             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1533           if (!filename)
1534             {
1535               printf(" no packages file entry, skipped\n");
1536               break;
1537             }
1538           printf(" fetching\n");
1539           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1540             break;      /* hopeless */
1541           repo_add_susetags(repo, fp, defvendor, 0, 0);
1542           fclose(fp);
1543           data = repo_add_repodata(repo, 0);
1544           susetags_add_ext(repo, data);
1545           repodata_internalize(data);
1546           if (!badchecksum)
1547             writecachedrepo(repo, 0, 0, cinfo->cookie);
1548           repodata_create_stubs(repo_last_repodata(repo));
1549           break;
1550         default:
1551           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1552           repo_free(repo, 1);
1553           cinfo->repo = 0;
1554           break;
1555         }
1556     }
1557   if (sigpool)
1558     pool_free(sigpool);
1559 }
1560
1561
1562 int
1563 str2archid(Pool *pool, char *arch)
1564 {
1565   Id id;
1566   if (!*arch)
1567     return 0;
1568   id = str2id(pool, arch, 0);
1569   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1570     return id;
1571   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1572     return 0;
1573   return id;
1574 }
1575
1576 int
1577 depglob(Pool *pool, char *name, Queue *job)
1578 {
1579   Id p, pp;
1580   Id id = str2id(pool, name, 0);
1581   int i, match = 0;
1582
1583   if (id)
1584     {
1585       FOR_PROVIDES(p, pp, id)
1586         {
1587           Solvable *s = pool->solvables + p;
1588           match = 1;
1589           if (s->name == id)
1590             {
1591               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1592               return 1;
1593             }
1594         }
1595       if (match)
1596         {
1597           printf("[using capability match for '%s']\n", name);
1598           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1599           return 1;
1600         }
1601     }
1602
1603   if (strpbrk(name, "[*?") == 0)
1604     return 0;
1605
1606   /* looks like a name glob. hard work. */
1607   for (p = 1; p < pool->nsolvables; p++)
1608     {
1609       Solvable *s = pool->solvables + p;
1610       if (!s->repo || !pool_installable(pool, s))
1611         continue;
1612       id = s->name;
1613       if (fnmatch(name, id2str(pool, id), 0) == 0)
1614         {
1615           for (i = 0; i < job->count; i += 2)
1616             if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1617               break;
1618           if (i == job->count)
1619             queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1620           match = 1;
1621         }
1622     }
1623   if (match)
1624     return 1;
1625   /* looks like a dep glob. really hard work. */
1626   for (id = 1; id < pool->ss.nstrings; id++)
1627     {
1628       if (!pool->whatprovides[id])
1629         continue;
1630       if (fnmatch(name, id2str(pool, id), 0) == 0)
1631         {
1632           if (!match)
1633             printf("[using capability match for '%s']\n", name);
1634           for (i = 0; i < job->count; i += 2)
1635             if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1636               break;
1637           if (i == job->count)
1638             queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1639           match = 1;
1640         }
1641     }
1642   if (match)
1643     return 1;
1644   return 0;
1645 }
1646
1647 void
1648 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1649 {
1650   int i;
1651   for (i = 0; i < job->count; i += 2)
1652     {
1653       if (job->elements[i] != SOLVER_SOLVABLE_NAME && job->elements[i] != SOLVER_SOLVABLE_PROVIDES)
1654         continue;
1655       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1656     }
1657 }
1658
1659 int
1660 limitevr(Pool *pool, char *evr, Queue *job, Id archid)
1661 {
1662   Queue mq;
1663   Id p, pp, evrid;
1664   int matched = 0;
1665   int i, j;
1666   Solvable *s;
1667   const char *sevr;
1668
1669   queue_init(&mq);
1670   for (i = 0; i < job->count; i += 2)
1671     {
1672       queue_empty(&mq);
1673       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1674         {
1675           s = pool_id2solvable(pool, p);
1676           if (archid && s->arch != archid)
1677             continue;
1678           sevr = id2str(pool, s->evr);
1679           if (!strchr(evr, ':') && strchr(sevr, ':'))
1680             sevr = strchr(sevr, ':') + 1;
1681           if (evrcmp_str(pool, sevr, evr, EVRCMP_MATCH) == 0)
1682              queue_push(&mq, p);
1683         }
1684       if (mq.count)
1685         {
1686           if (!matched && i)
1687             {
1688               queue_deleten(job, 0, i);
1689               i = 0;
1690             }
1691           matched = 1;
1692           /* if all solvables have the same evr */
1693           s = pool_id2solvable(pool, mq.elements[0]);
1694           evrid = s->evr;
1695           for (j = 0; j < mq.count; j++)
1696             {
1697               s = pool_id2solvable(pool, mq.elements[j]);
1698               if (s->evr != evrid)
1699                 break;
1700             }
1701           if (j == mq.count && j > 1)
1702             {
1703               prune_to_best_arch(pool, &mq);
1704               // prune_to_highest_prio(pool, &mq);
1705               mq.count = 1;
1706             }
1707           if (mq.count > 1)
1708             {
1709               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1710               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1711             }
1712           else
1713             {
1714               job->elements[i] = SOLVER_SOLVABLE;
1715               job->elements[i + 1] = mq.elements[0];
1716             }
1717         }
1718       else if (matched)
1719         {
1720           queue_deleten(job, i, 2);
1721           i -= 2;
1722         }
1723     }
1724   queue_free(&mq);
1725   if (matched)
1726     return 1;
1727   if (!archid)
1728     {
1729       char *r;
1730       if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1731         {
1732           *r = 0;
1733           if (limitevr(pool, evr, job, archid))
1734             {
1735               *r = '.';
1736               return 1;
1737             }
1738           *r = '.';
1739         }
1740     }
1741   return 0;
1742 }
1743
1744 int
1745 limitrepo(Pool *pool, Id repofilter, Queue *job)
1746 {
1747   Queue mq;
1748   Id p, pp;
1749   int matched = 0;
1750   int i;
1751   Solvable *s;
1752
1753   queue_init(&mq);
1754   for (i = 0; i < job->count; i += 2)
1755     {
1756       queue_empty(&mq);
1757       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1758         {
1759           s = pool_id2solvable(pool, p);
1760           if (s->repo && s->repo->repoid == repofilter)
1761              queue_push(&mq, p);
1762         }
1763       if (mq.count)
1764         {
1765           if (!matched && i)
1766             {
1767               queue_deleten(job, 0, i);
1768               i = 0;
1769             }
1770           matched = 1;
1771           if (mq.count > 1)
1772             {
1773               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1774               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1775             }
1776           else
1777             {
1778               job->elements[i] = SOLVER_SOLVABLE;
1779               job->elements[i + 1] = mq.elements[0];
1780             }
1781         }
1782       else if (matched)
1783         {
1784           queue_deleten(job, i, 2);
1785           i -= 2;
1786         }
1787     }
1788   queue_free(&mq);
1789   return matched;
1790 }
1791
1792 void
1793 mkselect(Pool *pool, int mode, char *name, Queue *job)
1794 {
1795   char *r, *r2;
1796   Id archid;
1797
1798   if (*name == '/')
1799     {
1800       Dataiterator di;
1801       Queue q;
1802       int match = 0;
1803
1804       queue_init(&q);
1805       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1806       while (dataiterator_step(&di))
1807         {
1808           Solvable *s = pool->solvables + di.solvid;
1809           if (!s->repo || !pool_installable(pool, s))
1810             continue;
1811           queue_push(&q, di.solvid);
1812           dataiterator_skip_solvable(&di);
1813         }
1814       dataiterator_free(&di);
1815       if (q.count)
1816         {
1817           printf("[using file list match for '%s']\n", name);
1818           match = 1;
1819           if (q.count > 1)
1820             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1821           else
1822             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1823         }
1824       queue_free(&q);
1825       if (match)
1826         return;
1827     }
1828   if ((r = strpbrk(name, "<=>")) != 0)
1829     {
1830       /* relation case, support:
1831        * depglob rel
1832        * depglob.rpm rel
1833        */
1834       int rflags = 0;
1835       int nend = r - name;
1836       for (; *r; r++)
1837         {
1838           if (*r == '<')
1839             rflags |= REL_LT;
1840           else if (*r == '=')
1841             rflags |= REL_EQ;
1842           else if (*r == '>')
1843             rflags |= REL_GT;
1844           else
1845             break;
1846         }
1847       while (*r && *r == ' ' && *r == '\t')
1848         r++;
1849       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1850         nend--;
1851       name[nend] = 0;
1852       if (!*name || !*r)
1853         {
1854           fprintf(stderr, "bad relation\n");
1855           exit(1);
1856         }
1857       if (depglob(pool, name, job))
1858         {
1859           addrelation(pool, job, rflags, str2id(pool, r, 1));
1860           return;
1861         }
1862       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1863         {
1864           *r2 = 0;
1865           if (depglob(pool, name, job))
1866             {
1867               *r2 = '.';
1868               addrelation(pool, job, REL_ARCH, archid);
1869               addrelation(pool, job, rflags, str2id(pool, r, 1));
1870               return;
1871             }
1872           *r2 = '.';
1873         }
1874     }
1875   else
1876     {
1877       /* no relation case, support:
1878        * depglob
1879        * depglob.arch
1880        * depglob-version-release
1881        * depglob-version-release.arch
1882        */
1883       if (depglob(pool, name, job))
1884         return;
1885       archid = 0;
1886       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1887         {
1888           *r = 0;
1889           if (depglob(pool, name, job))
1890             {
1891               *r = '.';
1892               addrelation(pool, job, REL_ARCH, archid);
1893               return;
1894             }
1895           *r = '.';
1896         }
1897       if ((r = strrchr(name, '-')) != 0)
1898         {
1899           *r = 0;
1900           if (depglob(pool, name, job))
1901             {
1902               /* have just the version */
1903               *r = '-';
1904               if (limitevr(pool, r + 1, job, 0))
1905                 return;
1906             }
1907           if ((r2 = strrchr(name, '-')) != 0)
1908             {
1909               *r = '-';
1910               *r2 = 0;
1911               if (depglob(pool, name, job))
1912                 {
1913                   *r2 = '-';
1914                   if (limitevr(pool, r2 + 1, job, 0))
1915                     return;
1916                 }
1917               *r2 = '-';
1918             }
1919           *r = '-';
1920         }
1921     }
1922   fprintf(stderr, "nothing matches '%s'\n", name);
1923   exit(1);
1924 }
1925
1926
1927 int
1928 yesno(const char *str)
1929 {
1930   char inbuf[128], *ip;
1931
1932   for (;;)
1933     {
1934       printf("%s", str);
1935       fflush(stdout);
1936       *inbuf = 0;
1937       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1938         {
1939           printf("Abort.\n");
1940           exit(1);
1941         }
1942       while (*ip == ' ' || *ip == '\t')
1943         ip++;
1944       if (*ip == 'q')
1945         {
1946           printf("Abort.\n");
1947           exit(1);
1948         }
1949       if (*ip == 'y' || *ip == 'n')
1950         return *ip == 'y' ? 1 : 0;
1951     }
1952 }
1953
1954 struct fcstate {
1955   FILE **newpkgsfps;
1956   Queue *checkq;
1957   int newpkgscnt;
1958   void *rpmdbstate;
1959 };
1960
1961 static void *
1962 fileconflict_cb(Pool *pool, Id p, void *cbdata)
1963 {
1964   struct fcstate *fcstate = cbdata;
1965   Solvable *s;
1966   Id rpmdbid;
1967   int i;
1968   FILE *fp;
1969
1970   if (!p)
1971     {
1972       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1973       return 0;
1974     }
1975   s = pool_id2solvable(pool, p);
1976   if (pool->installed && s->repo == pool->installed)
1977     {
1978       if (!s->repo->rpmdbid)
1979         return 0;
1980       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1981       if (!rpmdbid)
1982         return 0;
1983        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1984     }
1985   for (i = 0; i < fcstate->newpkgscnt; i++)
1986     if (fcstate->checkq->elements[i] == p)
1987       break;
1988   if (i == fcstate->newpkgscnt)
1989     return 0;
1990   fp = fcstate->newpkgsfps[i];
1991   if (!fp)
1992     return 0;
1993   rewind(fp);
1994   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1995 }
1996
1997 void
1998 runrpm(const char *arg, const char *name, int dupfd3)
1999 {
2000   pid_t pid;
2001   int status;
2002
2003   if ((pid = fork()) == (pid_t)-1)
2004     {
2005       perror("fork");
2006       exit(1);
2007     }
2008   if (pid == 0)
2009     {
2010       if (dupfd3 != -1 && dupfd3 != 3)
2011         {
2012           dup2(dupfd3, 3);
2013           close(dupfd3);
2014         }
2015       if (dupfd3 != -1)
2016         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2017       if (strcmp(arg, "-e") == 0)
2018         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2019       else
2020         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2021       perror("rpm");
2022       _exit(0);
2023     }
2024   while (waitpid(pid, &status, 0) != pid)
2025     ;
2026   if (status)
2027     {
2028       printf("rpm failed\n");
2029       exit(1);
2030     }
2031 }
2032
2033 static Id
2034 nscallback(Pool *pool, void *data, Id name, Id evr)
2035 {
2036   if (name == NAMESPACE_PRODUCTBUDDY)
2037     {    
2038       /* SUSE specific hack: each product has an associated rpm */
2039       Solvable *s = pool->solvables + evr; 
2040       Id p, pp, cap; 
2041       
2042       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
2043       if (!cap)
2044         return 0;
2045       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
2046       if (!cap)
2047         return 0;
2048       FOR_PROVIDES(p, pp, cap) 
2049         {
2050           Solvable *ps = pool->solvables + p; 
2051           if (ps->repo == s->repo && ps->arch == s->arch)
2052             break;
2053         }
2054       return p;
2055     }
2056   return 0;
2057 }
2058
2059 #ifdef SOFTLOCKS_PATH
2060
2061 void
2062 addsoftlocks(Pool *pool, Queue *job)
2063 {
2064   FILE *fp;
2065   Id type, id, p, pp;
2066   char *bp, *ep, buf[4096];
2067
2068   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2069     return;
2070   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2071     {
2072       while (*bp == ' ' || *bp == '\t')
2073         bp++;
2074       if (!*bp || *bp == '#')
2075         continue;
2076       for (ep = bp; *ep; ep++)
2077         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2078           break;
2079       *ep = 0;
2080       type = SOLVER_SOLVABLE_NAME;
2081       if (!strncmp(bp, "provides:", 9) && bp[9])
2082         {
2083           type = SOLVER_SOLVABLE_PROVIDES;
2084           bp += 9;
2085         }
2086       id = str2id(pool, bp, 1);
2087       if (pool->installed)
2088         {
2089           FOR_JOB_SELECT(p, pp, type, id)
2090             if (pool->solvables[p].repo == pool->installed)
2091               break;
2092           if (p)
2093             continue;   /* ignore, as it is already installed */
2094         }
2095       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2096     }
2097   fclose(fp);
2098 }
2099
2100 #endif
2101
2102
2103 void
2104 rewrite_repos(Pool *pool, Id *addedfileprovides)
2105 {
2106   Repo *repo;
2107   Repodata *data;
2108   Map providedids;
2109   Queue fileprovidesq;
2110   Id id;
2111   int i, j, n, nprovidedids;
2112   struct repoinfo *cinfo;
2113
2114   map_init(&providedids, pool->ss.nstrings);
2115   queue_init(&fileprovidesq);
2116   for (nprovidedids = 0; (id = addedfileprovides[nprovidedids]) != 0; nprovidedids++)
2117     MAPSET(&providedids, id);
2118   FOR_REPOS(i, repo)
2119     {
2120       /* make sure this repo has just one main repodata */
2121       if (!repo->nrepodata)
2122         continue;
2123       cinfo = repo->appdata;
2124       data = repo->repodata + 0;
2125       if (data->store.pagefd == -1)
2126         continue;
2127       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2128         {
2129           n = 0;
2130           for (j = 0; j < fileprovidesq.count; j++)
2131             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2132               n++;
2133           if (n == nprovidedids)
2134             continue;   /* nothing new added */
2135         }
2136       /* oh my! */
2137       for (j = 0; addedfileprovides[j]; j++)
2138         repodata_add_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[j]);
2139       repodata_internalize(data);
2140       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2141     }
2142   queue_free(&fileprovidesq);
2143   map_free(&providedids);
2144 }
2145
2146 #define MODE_LIST        0
2147 #define MODE_INSTALL     1
2148 #define MODE_ERASE       2
2149 #define MODE_UPDATE      3
2150 #define MODE_DISTUPGRADE 4
2151 #define MODE_VERIFY      5
2152 #define MODE_PATCH       6
2153 #define MODE_INFO        7
2154 #define MODE_REPOLIST    8
2155 #define MODE_SEARCH      9
2156
2157 void
2158 usage(int r)
2159 {
2160   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2161   fprintf(stderr, "\n");
2162   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2163   fprintf(stderr, "                  versions from the repositories\n");
2164   fprintf(stderr, "    erase:        erase installed packages\n");
2165   fprintf(stderr, "    info:         display package information\n");
2166   fprintf(stderr, "    install:      install packages\n");
2167   fprintf(stderr, "    list:         list packages\n");
2168   fprintf(stderr, "    repos:        list enabled repositories\n");
2169   fprintf(stderr, "    search:       search name/summary/description\n");
2170   fprintf(stderr, "    update:       update installed packages\n");
2171   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2172   fprintf(stderr, "\n");
2173   exit(r);
2174 }
2175
2176 int
2177 main(int argc, char **argv)
2178 {
2179   Pool *pool;
2180   Repo *commandlinerepo = 0;
2181   Id *commandlinepkgs = 0;
2182   Id p, pp;
2183   struct repoinfo *repoinfos;
2184   int nrepoinfos = 0;
2185   int mainmode = 0, mode = 0;
2186   int i, newpkgs;
2187   Queue job, checkq;
2188   Solver *solv = 0;
2189   Transaction *trans;
2190   char inbuf[128], *ip;
2191   int allpkgs = 0;
2192   FILE **newpkgsfps;
2193   struct fcstate fcstate;
2194   Id *addedfileprovides = 0;
2195   Id repofilter = 0;
2196
2197   argc--;
2198   argv++;
2199   if (!argv[0])
2200     usage(1);
2201   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2202     {
2203       mainmode = MODE_INSTALL;
2204       mode = SOLVER_INSTALL;
2205     }
2206   else if (!strcmp(argv[0], "patch"))
2207     {
2208       mainmode = MODE_PATCH;
2209       mode = SOLVER_INSTALL;
2210     }
2211   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2212     {
2213       mainmode = MODE_ERASE;
2214       mode = SOLVER_ERASE;
2215     }
2216   else if (!strcmp(argv[0], "list"))
2217     {
2218       mainmode = MODE_LIST;
2219       mode = 0;
2220     }
2221   else if (!strcmp(argv[0], "info"))
2222     {
2223       mainmode = MODE_INFO;
2224       mode = 0;
2225     }
2226   else if (!strcmp(argv[0], "search"))
2227     {
2228       mainmode = MODE_SEARCH;
2229       mode = 0;
2230     }
2231   else if (!strcmp(argv[0], "verify"))
2232     {
2233       mainmode = MODE_VERIFY;
2234       mode = SOLVER_VERIFY;
2235     }
2236   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2237     {
2238       mainmode = MODE_UPDATE;
2239       mode = SOLVER_UPDATE;
2240     }
2241   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2242     {
2243       mainmode = MODE_DISTUPGRADE;
2244       mode = SOLVER_UPDATE;
2245     }
2246   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2247     {
2248       mainmode = MODE_REPOLIST;
2249       mode = 0;
2250     }
2251   else
2252     usage(1);
2253
2254   pool = pool_create();
2255 #ifdef FEDORA
2256   pool->obsoleteusescolors = 1;
2257 #endif
2258   pool_setloadcallback(pool, load_stub, 0);
2259   pool->nscallback = nscallback;
2260   // pool_setdebuglevel(pool, 2);
2261   setarch(pool);
2262   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
2263
2264   if (mainmode == MODE_REPOLIST)
2265     {
2266       int j = 1;
2267       for (i = 0; i < nrepoinfos; i++)
2268         {
2269           struct repoinfo *cinfo = repoinfos + i;
2270           if (!cinfo->enabled)
2271             continue;
2272           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
2273         }
2274       exit(0);
2275     }
2276
2277   read_repos(pool, repoinfos, nrepoinfos);
2278
2279   if (argc > 2 && !strcmp(argv[1], "-r"))
2280     {
2281       const char *rname = argv[2], *rp;
2282       for (rp = rname; *rp; rp++)
2283         if (*rp <= '0' || *rp >= '9')
2284           break;
2285       if (!*rp)
2286         {
2287           /* repo specified by number */
2288           int rnum = atoi(rname);
2289           for (i = 0; i < nrepoinfos; i++)
2290             {
2291               struct repoinfo *cinfo = repoinfos + i;
2292               if (!cinfo->enabled)
2293                 continue;
2294               if (--rnum == 0)
2295                 repofilter = cinfo->repo->repoid;
2296             }
2297         }
2298       else
2299         {
2300           /* repo specified by alias */
2301           Repo *repo;
2302           FOR_REPOS(i, repo)
2303             {
2304               if (!strcasecmp(rname, repo->name))
2305                 repofilter = repo->repoid;
2306             }
2307         }
2308       if (!repofilter)
2309         {
2310           fprintf(stderr, "%s: no such repo\n", rname);
2311           exit(1);
2312         }
2313       argc -= 2;
2314       argv += 2;
2315     }
2316   if (mainmode == MODE_SEARCH)
2317     {
2318       Dataiterator di;
2319       Map m;
2320       if (argc != 2)
2321         usage(1);
2322       map_init(&m, pool->nsolvables);
2323       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2324       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2325       dataiterator_set_search(&di, 0, 0);
2326       while (dataiterator_step(&di))
2327         MAPSET(&m, di.solvid);
2328       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2329       dataiterator_set_search(&di, 0, 0);
2330       while (dataiterator_step(&di))
2331         MAPSET(&m, di.solvid);
2332       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2333       dataiterator_set_search(&di, 0, 0);
2334       while (dataiterator_step(&di))
2335         MAPSET(&m, di.solvid);
2336       dataiterator_free(&di);
2337
2338       for (p = 1; p < pool->nsolvables; p++)
2339         {
2340           Solvable *s = pool_id2solvable(pool, p);
2341           if (!MAPTST(&m, p))
2342             continue;
2343           printf("  - %s: %s\n", solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2344         }
2345       map_free(&m);
2346       exit(0);
2347     }
2348
2349
2350   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2351     {
2352       for (i = 1; i < argc; i++)
2353         {
2354           int l;
2355           l = strlen(argv[i]);
2356           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2357             continue;
2358           if (access(argv[i], R_OK))
2359             {
2360               perror(argv[i]);
2361               exit(1);
2362             }
2363           if (!commandlinepkgs)
2364             commandlinepkgs = sat_calloc(argc, sizeof(Id));
2365           if (!commandlinerepo)
2366             commandlinerepo = repo_create(pool, "@commandline");
2367           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2368           commandlinepkgs[i] = commandlinerepo->end - 1;
2369         }
2370       if (commandlinerepo)
2371         repo_internalize(commandlinerepo);
2372     }
2373
2374   // FOR_REPOS(i, repo)
2375   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2376   addedfileprovides = 0;
2377   pool_addfileprovides_ids(pool, 0, &addedfileprovides);
2378   if (addedfileprovides && *addedfileprovides)
2379     rewrite_repos(pool, addedfileprovides);
2380   sat_free(addedfileprovides);
2381   pool_createwhatprovides(pool);
2382
2383   queue_init(&job);
2384   for (i = 1; i < argc; i++)
2385     {
2386       Queue job2;
2387       int j;
2388
2389       if (commandlinepkgs && commandlinepkgs[i])
2390         {
2391           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2392           continue;
2393         }
2394       queue_init(&job2);
2395       mkselect(pool, mode, argv[i], &job2);
2396       if (repofilter && !limitrepo(pool, repofilter, &job2))
2397         {
2398           fprintf(stderr, "nothing in repo matches '%s'\n", argv[i]);
2399           exit(1);
2400         }
2401       for (j = 0; j < job2.count; j++)
2402         queue_push(&job, job2.elements[j]);
2403       queue_free(&job2);
2404     }
2405
2406   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2407     {
2408       printf("no package matched\n");
2409       exit(1);
2410     }
2411
2412   if (!job.count)
2413     allpkgs = 1;
2414
2415   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2416     {
2417       /* list mode, no solver needed */
2418       for (i = 0; i < job.count; i += 2)
2419         {
2420           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2421             {
2422               Solvable *s = pool_id2solvable(pool, p);
2423               if (mainmode == MODE_INFO)
2424                 {
2425                   printf("Name:        %s\n", solvable2str(pool, s));
2426                   printf("Repo:        %s\n", s->repo->name);
2427                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2428                   printf("Url:         %s\n", solvable_lookup_str(s, SOLVABLE_URL));
2429                   printf("License:     %s\n", solvable_lookup_str(s, SOLVABLE_LICENSE));
2430                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2431                   printf("\n");
2432                 }
2433               else
2434                 {
2435                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
2436                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2437                   if (sum)
2438                     printf("    %s\n", sum);
2439                 }
2440             }
2441         }
2442       queue_free(&job);
2443       pool_free(pool);
2444       free_repoinfos(repoinfos, nrepoinfos);
2445       sat_free(commandlinepkgs);
2446 #ifdef FEDORA
2447       yum_substitute(pool, 0);
2448 #endif
2449       exit(0);
2450     }
2451
2452   if (mainmode == MODE_PATCH)
2453     {
2454       int pruneyou = 0;
2455       Map installedmap;
2456       Solvable *s;
2457
2458       map_init(&installedmap, pool->nsolvables);
2459       if (pool->installed)
2460         FOR_REPO_SOLVABLES(pool->installed, p, s)
2461           MAPSET(&installedmap, p);
2462
2463       /* install all patches */
2464       for (p = 1; p < pool->nsolvables; p++)
2465         {
2466           const char *type;
2467           int r;
2468           Id p2;
2469
2470           s = pool->solvables + p;
2471           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2472             continue;
2473           FOR_PROVIDES(p2, pp, s->name)
2474             {
2475               Solvable *s2 = pool->solvables + p2;
2476               if (s2->name != s->name)
2477                 continue;
2478               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2479               if (r < 0 || (r == 0 && p > p2))
2480                 break;
2481             }
2482           if (p2)
2483             continue;
2484           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2485           if (type && !strcmp(type, "optional"))
2486             continue;
2487           r = solvable_trivial_installable_map(s, &installedmap, 0);
2488           if (r == -1)
2489             continue;
2490           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2491             {
2492               if (!pruneyou++)
2493                 queue_empty(&job);
2494             }
2495           else if (pruneyou)
2496             continue;
2497           queue_push2(&job, SOLVER_SOLVABLE, p);
2498         }
2499       map_free(&installedmap);
2500     }
2501
2502   // add mode
2503   for (i = 0; i < job.count; i += 2)
2504     {
2505       if (mode == SOLVER_UPDATE)
2506         {
2507           /* make update of not installed packages an install */
2508           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2509             if (pool->installed && pool->solvables[p].repo == pool->installed)
2510               break;
2511           if (!p)
2512             {
2513               job.elements[i] |= SOLVER_INSTALL;
2514               continue;
2515             }
2516         }
2517       job.elements[i] |= mode;
2518     }
2519
2520   if (mainmode == MODE_DISTUPGRADE && allpkgs && repofilter)
2521     queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter);
2522
2523   // multiversion test
2524   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2525   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2526   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2527
2528 #ifdef SOFTLOCKS_PATH
2529   addsoftlocks(pool, &job);
2530 #endif
2531
2532 rerunsolver:
2533   for (;;)
2534     {
2535       Id problem, solution;
2536       int pcnt, scnt;
2537
2538       solv = solver_create(pool);
2539       solv->ignorealreadyrecommended = 1;
2540       solv->updatesystem = allpkgs && !repofilter && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
2541       solv->dosplitprovides = solv->updatesystem;
2542       solv->fixsystem = allpkgs && !repofilter && mainmode == MODE_VERIFY;
2543       if (mainmode == MODE_DISTUPGRADE && allpkgs && !repofilter)
2544         {
2545           solv->distupgrade = 1;
2546           solv->allowdowngrade = 1;
2547           solv->allowarchchange = 1;
2548           solv->allowvendorchange = 1;
2549         }
2550       if (mainmode == MODE_ERASE)
2551         solv->allowuninstall = 1;       /* don't nag */
2552
2553       solver_solve(solv, &job);
2554       if (!solv->problems.count)
2555         break;
2556       pcnt = solver_problem_count(solv);
2557       printf("Found %d problems:\n", pcnt);
2558       for (problem = 1; problem <= pcnt; problem++)
2559         {
2560           int take = 0;
2561           printf("Problem %d:\n", problem);
2562           solver_printprobleminfo(solv, problem);
2563           printf("\n");
2564           scnt = solver_solution_count(solv, problem);
2565           for (solution = 1; solution <= scnt; solution++)
2566             {
2567               printf("Solution %d:\n", solution);
2568               solver_printsolution(solv, problem, solution);
2569               printf("\n");
2570             }
2571           for (;;)
2572             {
2573               printf("Please choose a solution: ");
2574               fflush(stdout);
2575               *inbuf = 0;
2576               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2577                 {
2578                   printf("Abort.\n");
2579                   exit(1);
2580                 }
2581               while (*ip == ' ' || *ip == '\t')
2582                 ip++;
2583               if (*ip >= '0' && *ip <= '9')
2584                 {
2585                   take = atoi(ip);
2586                   if (take >= 1 && take <= scnt)
2587                     break;
2588                 }
2589               if (*ip == 's')
2590                 {
2591                   take = 0;
2592                   break;
2593                 }
2594               if (*ip == 'q')
2595                 {
2596                   printf("Abort.\n");
2597                   exit(1);
2598                 }
2599             }
2600           if (!take)
2601             continue;
2602           solver_take_solution(solv, problem, take, &job);
2603         }
2604       solver_free(solv);
2605       solv = 0;
2606     }
2607
2608   trans = &solv->trans;
2609   if (!trans->steps.count)
2610     {
2611       printf("Nothing to do.\n");
2612       exit(1);
2613     }
2614   printf("\n");
2615   printf("Transaction summary:\n\n");
2616   solver_printtransaction(solv);
2617
2618 #ifndef FEDORA
2619   if (1)
2620     {
2621       DUChanges duc[4];
2622       int i;
2623
2624       duc[0].path = "/";
2625       duc[1].path = "/usr/share/man";
2626       duc[2].path = "/sbin";
2627       duc[3].path = "/etc";
2628       transaction_calc_duchanges(trans, duc, 4);
2629       for (i = 0; i < 4; i++)
2630         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2631     }
2632 #endif
2633   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
2634   printf("\n");
2635
2636   if (!yesno("OK to continue (y/n)? "))
2637     {
2638       printf("Abort.\n");
2639       exit(1);
2640     }
2641
2642   queue_init(&checkq);
2643   newpkgs = transaction_installedresult(trans, &checkq);
2644   newpkgsfps = 0;
2645
2646   if (newpkgs)
2647     {
2648       int downloadsize = 0;
2649       for (i = 0; i < newpkgs; i++)
2650         {
2651           Solvable *s;
2652
2653           p = checkq.elements[i];
2654           s = pool_id2solvable(pool, p);
2655           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
2656         }
2657       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
2658       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
2659       for (i = 0; i < newpkgs; i++)
2660         {
2661           unsigned int medianr;
2662           char *loc;
2663           Solvable *s;
2664           struct repoinfo *cinfo;
2665           const unsigned char *chksum;
2666           Id chksumtype;
2667           Dataiterator di;
2668
2669           p = checkq.elements[i];
2670           s = pool_id2solvable(pool, p);
2671           if (s->repo == commandlinerepo)
2672             {
2673               loc = solvable_get_location(s, &medianr);
2674               if (!(newpkgsfps[i] = fopen(loc, "r")))
2675                 {
2676                   perror(loc);
2677                   exit(1);
2678                 }
2679               putchar('.');
2680               continue;
2681             }
2682           cinfo = s->repo->appdata;
2683           if (!cinfo)
2684             {
2685               printf("%s: no repository information\n", s->repo->name);
2686               exit(1);
2687             }
2688           loc = solvable_get_location(s, &medianr);
2689           if (!loc)
2690              continue;
2691
2692           if (pool->installed && pool->installed->nsolvables)
2693             {
2694               /* try a delta first */
2695               char *matchname = strdup(id2str(pool, s->name));
2696               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2697               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2698               while (dataiterator_step(&di))
2699                 {
2700                   Id baseevr, op;
2701
2702                   dataiterator_setpos_parent(&di);
2703                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2704                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2705                     continue;
2706                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2707                   FOR_PROVIDES(op, pp, s->name)
2708                     {
2709                       Solvable *os = pool->solvables + op;
2710                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2711                         break;
2712                     }
2713                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2714                     {
2715                       /* base is installed, run sequence check */
2716                       const char *seqname;
2717                       const char *seqevr;
2718                       const char *seqnum;
2719                       const char *seq;
2720                       const char *dloc;
2721                       FILE *fp;
2722                       char cmd[128];
2723                       int newfd;
2724
2725                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2726                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2727                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2728                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2729                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2730 #ifdef FEDORA
2731                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
2732 #else
2733                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
2734 #endif
2735                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
2736                         continue;       /* didn't match */
2737                       /* looks good, download delta */
2738                       chksumtype = 0;
2739                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2740                       if (!chksumtype)
2741                         continue;       /* no way! */
2742                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2743                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2744                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2745                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2746                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2747                         continue;
2748                       /* got it, now reconstruct */
2749                       newfd = opentmpfile();
2750 #ifdef FEDORA
2751                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
2752 #else
2753                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2754 #endif
2755                       fcntl(fileno(fp), F_SETFD, 0);
2756                       if (system(cmd))
2757                         {
2758                           close(newfd);
2759                           fclose(fp);
2760                           continue;
2761                         }
2762                       lseek(newfd, 0, SEEK_SET);
2763                       chksumtype = 0;
2764                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2765                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2766                         {
2767                           close(newfd);
2768                           fclose(fp);
2769                           continue;
2770                         }
2771                       newpkgsfps[i] = fdopen(newfd, "r");
2772                       fclose(fp);
2773                       break;
2774                     }
2775                 }
2776               dataiterator_free(&di);
2777               sat_free(matchname);
2778             }
2779           
2780           if (newpkgsfps[i])
2781             {
2782               putchar('d');
2783               fflush(stdout);
2784               continue;         /* delta worked! */
2785             }
2786           if (cinfo->type == TYPE_SUSETAGS)
2787             {
2788               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2789               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2790             }
2791           chksumtype = 0;
2792           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2793           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
2794             {
2795               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2796               exit(1);
2797             }
2798           putchar('.');
2799           fflush(stdout);
2800         }
2801       putchar('\n');
2802     }
2803
2804   if (newpkgs)
2805     {
2806       Queue conflicts;
2807
2808       printf("Searching for file conflicts\n");
2809       queue_init(&conflicts);
2810       fcstate.rpmdbstate = 0;
2811       fcstate.newpkgscnt = newpkgs;
2812       fcstate.checkq = &checkq;
2813       fcstate.newpkgsfps = newpkgsfps;
2814       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
2815       if (conflicts.count)
2816         {
2817           printf("\n");
2818           for (i = 0; i < conflicts.count; i += 5)
2819             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]));
2820           printf("\n");
2821           if (yesno("Re-run solver (y/n/q)? "))
2822             {
2823               for (i = 0; i < newpkgs; i++)
2824                 if (newpkgsfps[i])
2825                   fclose(newpkgsfps[i]);
2826               newpkgsfps = sat_free(newpkgsfps);
2827               solver_free(solv);
2828               pool_add_fileconflicts_deps(pool, &conflicts);
2829               pool_createwhatprovides(pool);    /* Hmm... */
2830               goto rerunsolver;
2831             }
2832         }
2833       queue_free(&conflicts);
2834     }
2835
2836   printf("Committing transaction:\n\n");
2837   transaction_order(trans, 0);
2838   for (i = 0; i < trans->steps.count; i++)
2839     {
2840       const char *evr, *evrp, *nvra;
2841       Solvable *s;
2842       int j;
2843       FILE *fp;
2844
2845       p = trans->steps.elements[i];
2846       s = pool_id2solvable(pool, p);
2847       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2848       switch(type)
2849         {
2850         case SOLVER_TRANSACTION_ERASE:
2851           printf("erase %s\n", solvid2str(pool, p));
2852           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2853             continue;
2854           /* strip epoch from evr */
2855           evr = evrp = id2str(pool, s->evr);
2856           while (*evrp >= '0' && *evrp <= '9')
2857             evrp++;
2858           if (evrp > evr && evrp[0] == ':' && evrp[1])
2859             evr = evrp + 1;
2860           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2861           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2862           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2863           break;
2864         case SOLVER_TRANSACTION_INSTALL:
2865         case SOLVER_TRANSACTION_MULTIINSTALL:
2866           printf("install %s\n", solvid2str(pool, p));
2867           for (j = 0; j < newpkgs; j++)
2868             if (checkq.elements[j] == p)
2869               break;
2870           fp = j < newpkgs ? newpkgsfps[j] : 0;
2871           if (!fp)
2872             continue;
2873           rewind(fp);
2874           lseek(fileno(fp), 0, SEEK_SET);
2875           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2876           fclose(fp);
2877           newpkgsfps[j] = 0;
2878           break;
2879         default:
2880           break;
2881         }
2882     }
2883
2884   for (i = 0; i < newpkgs; i++)
2885     if (newpkgsfps[i])
2886       fclose(newpkgsfps[i]);
2887   sat_free(newpkgsfps);
2888   queue_free(&checkq);
2889   solver_free(solv);
2890   queue_free(&job);
2891   pool_free(pool);
2892   free_repoinfos(repoinfos, nrepoinfos);
2893   sat_free(commandlinepkgs);
2894 #ifdef FEDORA
2895   yum_substitute(pool, 0);
2896 #endif
2897   exit(0);
2898 }