- add SOLVER_NOAUTOSET to disable automatic SET deduction
[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     {
928       flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
929       if (strcmp(repoext, "DL") != 0)
930         flags |= REPO_LOCALPOOL;        /* no local pool for DL so that we can compare IDs */
931     }
932
933   if (repo_add_solv_flags(repo, fp, flags))
934     {
935       fclose(fp);
936       return 0;
937     }
938   if (cinfo && !repoext)
939     {
940       memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
941       memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
942     }
943   if (mark)
944     futimes(fileno(fp), 0);     /* try to set modification time */
945   fclose(fp);
946   return 1;
947 }
948
949 void
950 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
951 {
952   FILE *fp;
953   int i, fd;
954   char *tmpl;
955   struct repoinfo *cinfo;
956   int onepiece;
957
958   cinfo = repo->appdata;
959   mkdir(SOLVCACHE_PATH, 0755);
960   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
961   fd = mkstemp(tmpl);
962   if (fd < 0)
963     {
964       free(tmpl);
965       return;
966     }
967   fchmod(fd, 0444);
968   if (!(fp = fdopen(fd, "w")))
969     {
970       close(fd);
971       unlink(tmpl);
972       free(tmpl);
973       return;
974     }
975
976   onepiece = 1;
977   for (i = repo->start; i < repo->end; i++)
978    if (repo->pool->solvables[i].repo != repo)
979      break;
980   if (i < repo->end)
981     onepiece = 0;
982
983   if (!info)
984     repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
985   else if (repoext)
986     repodata_write(info, fp, repo_write_stdkeyfilter, 0);
987   else
988     {
989       int oldnrepodata = repo->nrepodata;
990       repo->nrepodata = 1;      /* XXX: do this right */
991       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
992       repo->nrepodata = oldnrepodata;
993       onepiece = 0;
994     }
995
996   if (!repoext && cinfo)
997     {
998       if (!cinfo->extcookie[0])
999         {
1000           /* create the ext cookie and append it */
1001           /* we just need some unique ID */
1002           struct stat stb;
1003           if (!fstat(fileno(fp), &stb))
1004             {
1005               int i;
1006
1007               calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
1008               for (i = 0; i < 32; i++)
1009                 cinfo->extcookie[i] ^= cookie[i];
1010             }
1011           if (cinfo->extcookie[0] == 0)
1012             cinfo->extcookie[0] = 1;
1013         }
1014       if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
1015         {
1016           fclose(fp);
1017           unlink(tmpl);
1018           free(tmpl);
1019           return;
1020         }
1021     }
1022   /* append our cookie describing the metadata state */
1023   if (fwrite(cookie, 32, 1, fp) != 1)
1024     {
1025       fclose(fp);
1026       unlink(tmpl);
1027       free(tmpl);
1028       return;
1029     }
1030   if (fclose(fp))
1031     {
1032       unlink(tmpl);
1033       free(tmpl);
1034       return;
1035     }
1036   if (onepiece)
1037     {
1038       /* switch to just saved repo to activate paging and save memory */
1039       FILE *fp = fopen(tmpl, "r");
1040       if (fp)
1041         {
1042           if (!repoext)
1043             {
1044               /* main repo */
1045               repo_empty(repo, 1);
1046               if (repo_add_solv_flags(repo, fp, SOLV_ADD_NO_STUBS))
1047                 {
1048                   /* oops, no way to recover from here */
1049                   fprintf(stderr, "internal error\n");
1050                   exit(1);
1051                 }
1052             }
1053           else
1054             {
1055               /* make sure repodata contains complete repo */
1056               /* (this is how repodata_write saves it) */
1057               repodata_extend_block(info, repo->start, repo->end - repo->start);
1058               info->state = REPODATA_LOADING;
1059               /* no need for LOCALPOOL as pool already contains ids */
1060               repo_add_solv_flags(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1061               info->state = REPODATA_AVAILABLE; /* in case the load failed */
1062             }
1063           fclose(fp);
1064         }
1065     }
1066   if (!rename(tmpl, calccachepath(repo, repoext)))
1067     unlink(tmpl);
1068   free(tmpl);
1069 }
1070
1071
1072 static Pool *
1073 read_sigs()
1074 {
1075   Pool *sigpool = pool_create();
1076   Repo *repo = repo_create(sigpool, "rpmdbkeys");
1077   repo_add_rpmdb_pubkeys(repo, 0, 0);
1078   return sigpool;
1079 }
1080
1081
1082 /* repomd helpers */
1083
1084 static inline const char *
1085 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1086 {
1087   Pool *pool = repo->pool;
1088   Dataiterator di;
1089   const char *filename;
1090
1091   filename = 0;
1092   *chksump = 0;
1093   *chksumtypep = 0;
1094   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1095   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1096   if (dataiterator_step(&di))
1097     {
1098       dataiterator_setpos_parent(&di);
1099       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1100       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
1101     }
1102   dataiterator_free(&di);
1103   if (filename && !*chksumtypep)
1104     {
1105       printf("no %s file checksum!\n", what);
1106       filename = 0;
1107     }
1108   return filename;
1109 }
1110
1111 int
1112 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
1113 {
1114   Pool *pool = repo->pool;
1115   Dataiterator di;
1116   Id chksumtype, handle;
1117   const unsigned char *chksum;
1118   const char *filename;
1119
1120   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1121   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1122   if (!dataiterator_step(&di))
1123     {
1124       dataiterator_free(&di);
1125       return 0;
1126     }
1127   if (!strcmp(what, "prestodelta"))
1128     what = "deltainfo";
1129   dataiterator_setpos_parent(&di);
1130   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1131   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
1132   if (!filename || !chksum)
1133     {
1134       dataiterator_free(&di);
1135       return 0;
1136     }
1137   handle = repodata_new_handle(data);
1138   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1139   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1140   if (chksumtype)
1141     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1142   if (!strcmp(what, "deltainfo"))
1143     {
1144       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1145       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1146     }
1147   if (!strcmp(what, "filelists"))
1148     {
1149       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1150       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1151     }
1152   dataiterator_free(&di);
1153   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1154   return 1;
1155 }
1156
1157
1158 /* susetags helpers */
1159
1160 static inline const char *
1161 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1162 {
1163   Pool *pool = repo->pool;
1164   Dataiterator di;
1165   const char *filename;
1166
1167   filename = 0;
1168   *chksump = 0;
1169   *chksumtypep = 0;
1170   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1171   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1172   if (dataiterator_step(&di))
1173     {
1174       dataiterator_setpos_parent(&di);
1175       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1176       filename = what;
1177     }
1178   dataiterator_free(&di);
1179   if (filename && !*chksumtypep)
1180     {
1181       printf("no %s file checksum!\n", what);
1182       filename = 0;
1183     }
1184   return filename;
1185 }
1186
1187 static Id susetags_langtags[] = {
1188   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1189   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1190   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1191   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1192   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1193   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1194   0, 0
1195 };
1196
1197 void
1198 susetags_add_ext(Repo *repo, Repodata *data)
1199 {
1200   Pool *pool = repo->pool;
1201   Dataiterator di;
1202   char ext[3];
1203   Id handle, filechksumtype;
1204   const unsigned char *filechksum;
1205   int i;
1206
1207   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1208   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1209   while (dataiterator_step(&di))
1210     {
1211       if (strncmp(di.kv.str, "packages.", 9) != 0)
1212         continue;
1213       if (!strcmp(di.kv.str + 9, "gz"))
1214         continue;
1215       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1216         continue;
1217       ext[0] = di.kv.str[9];
1218       ext[1] = di.kv.str[10];
1219       ext[2] = 0;
1220       if (!strcmp(ext, "en"))
1221         continue;
1222       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1223         continue;
1224       handle = repodata_new_handle(data);
1225       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1226       if (filechksumtype)
1227         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1228       if (!strcmp(ext, "DU"))
1229         {
1230           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1231           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1232         }
1233       else if (!strcmp(ext, "FL"))
1234         {
1235           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1236           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1237         }
1238       else
1239         {
1240           for (i = 0; susetags_langtags[i]; i += 2)
1241             {
1242               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1243               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1244             }
1245         }
1246       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1247     }
1248   dataiterator_free(&di);
1249 }
1250
1251
1252 static inline int
1253 iscompressed(const char *name)
1254 {
1255   int l = strlen(name);
1256   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
1257 }
1258
1259
1260 /* load callback */
1261
1262 int
1263 load_stub(Pool *pool, Repodata *data, void *dp)
1264 {
1265   const char *filename, *descrdir, *repomdtype;
1266   const unsigned char *filechksum;
1267   Id filechksumtype;
1268   struct repoinfo *cinfo;
1269   FILE *fp;
1270   Id defvendor;
1271   char ext[3];
1272
1273   cinfo = data->repo->appdata;
1274
1275   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1276   if (filename)
1277     {
1278       /* susetags load */
1279       ext[0] = filename[9];
1280       ext[1] = filename[10];
1281       ext[2] = 0;
1282 #if 1
1283       printf("[%s:%s", data->repo->name, ext);
1284 #endif
1285       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1286         {
1287           printf(" cached]\n"); fflush(stdout);
1288           return 1;
1289         }
1290 #if 1
1291       printf(" fetching]\n"); fflush(stdout);
1292 #endif
1293       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1294       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
1295       if (!descrdir)
1296         descrdir = "suse/setup/descr";
1297       filechksumtype = 0;
1298       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1299       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1300         return 0;
1301       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1302       fclose(fp);
1303       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1304       return 1;
1305     }
1306
1307   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1308   if (repomdtype)
1309     {
1310       if (!strcmp(repomdtype, "filelists"))
1311         strcpy(ext, "FL");
1312       else if (!strcmp(repomdtype, "deltainfo"))
1313         strcpy(ext, "DL");
1314       else
1315         return 0;
1316 #if 1
1317       printf("[%s:%s", data->repo->name, ext);
1318 #endif
1319       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1320         {
1321           printf(" cached]\n");fflush(stdout);
1322           return 1;
1323         }
1324       printf(" fetching]\n"); fflush(stdout);
1325       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1326       filechksumtype = 0;
1327       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1328       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1329         return 0;
1330       if (!strcmp(ext, "FL"))
1331         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1332       else if (!strcmp(ext, "DL"))
1333         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1334       fclose(fp);
1335       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1336       return 1;
1337     }
1338
1339   return 0;
1340 }
1341
1342 static unsigned char installedcookie[32];
1343
1344 void
1345 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1346 {
1347   Repo *repo;
1348   struct repoinfo *cinfo;
1349   int i;
1350   FILE *fp;
1351   FILE *sigfp;
1352   const char *filename;
1353   const unsigned char *filechksum;
1354   Id filechksumtype;
1355   const char *descrdir;
1356   int defvendor;
1357   struct stat stb;
1358   Pool *sigpool = 0;
1359   Repodata *data;
1360   int badchecksum;
1361   int dorefresh;
1362
1363   repo = repo_create(pool, "@System");
1364   printf("rpm database:");
1365   if (stat("/var/lib/rpm/Packages", &stb))
1366     memset(&stb, 0, sizeof(&stb));
1367   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, installedcookie);
1368   if (usecachedrepo(repo, 0, installedcookie, 0))
1369     printf(" cached\n");
1370   else
1371     {
1372       FILE *ofp;
1373       printf(" reading\n");
1374       int done = 0;
1375
1376 #ifdef PRODUCTS_PATH
1377       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1378 #endif
1379       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1380         {
1381           Repo *ref = repo_create(pool, "@System.old");
1382           if (!repo_add_solv(ref, ofp))
1383             {
1384               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1385               done = 1;
1386             }
1387           fclose(ofp);
1388           repo_free(ref, 1);
1389         }
1390       if (!done)
1391         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1392       writecachedrepo(repo, 0, 0, installedcookie);
1393     }
1394   pool_set_installed(pool, repo);
1395
1396   for (i = 0; i < nrepoinfos; i++)
1397     {
1398       cinfo = repoinfos + i;
1399       if (!cinfo->enabled)
1400         continue;
1401
1402       repo = repo_create(pool, cinfo->alias);
1403       cinfo->repo = repo;
1404       repo->appdata = cinfo;
1405       repo->priority = 99 - cinfo->priority;
1406
1407       dorefresh = cinfo->autorefresh;
1408       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1409         {
1410           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1411             dorefresh = 0;
1412         }
1413       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1414         {
1415           printf("repo '%s':", cinfo->alias);
1416           printf(" cached\n");
1417           continue;
1418         }
1419       badchecksum = 0;
1420       switch (cinfo->type)
1421         {
1422         case TYPE_RPMMD:
1423           printf("rpmmd repo '%s':", cinfo->alias);
1424           fflush(stdout);
1425           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1426             {
1427               printf(" no repomd.xml file, skipped\n");
1428               repo_free(repo, 1);
1429               cinfo->repo = 0;
1430               break;
1431             }
1432           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1433           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1434             {
1435               printf(" cached\n");
1436               fclose(fp);
1437               break;
1438             }
1439           if (cinfo->repo_gpgcheck)
1440             {
1441               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1442               if (!sigfp)
1443                 {
1444                   printf(" unsigned, skipped\n");
1445                   fclose(fp);
1446                   break;
1447                 }
1448               if (!sigpool)
1449                 sigpool = read_sigs();
1450               if (!checksig(sigpool, fp, sigfp))
1451                 {
1452                   printf(" checksig failed, skipped\n");
1453                   fclose(sigfp);
1454                   fclose(fp);
1455                   break;
1456                 }
1457               fclose(sigfp);
1458             }
1459           repo_add_repomdxml(repo, fp, 0);
1460           fclose(fp);
1461           printf(" fetching\n");
1462           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1463           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1464             {
1465               repo_add_rpmmd(repo, fp, 0, 0);
1466               fclose(fp);
1467             }
1468           if (badchecksum)
1469             break;      /* hopeless */
1470
1471           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1472           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1473             {
1474               repo_add_updateinfoxml(repo, fp, 0);
1475               fclose(fp);
1476             }
1477
1478           data = repo_add_repodata(repo, 0);
1479           if (!repomd_add_ext(repo, data, "deltainfo"))
1480             repomd_add_ext(repo, data, "prestodelta");
1481           repomd_add_ext(repo, data, "filelists");
1482           repodata_internalize(data);
1483           if (!badchecksum)
1484             writecachedrepo(repo, 0, 0, cinfo->cookie);
1485           repodata_create_stubs(repo_last_repodata(repo));
1486           break;
1487
1488         case TYPE_SUSETAGS:
1489           printf("susetags repo '%s':", cinfo->alias);
1490           fflush(stdout);
1491           descrdir = 0;
1492           defvendor = 0;
1493           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1494             {
1495               printf(" no content file, skipped\n");
1496               repo_free(repo, 1);
1497               cinfo->repo = 0;
1498               break;
1499             }
1500           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1501           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1502             {
1503               printf(" cached\n");
1504               fclose(fp);
1505               break;
1506             }
1507           if (cinfo->repo_gpgcheck)
1508             {
1509               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1510               if (!sigfp)
1511                 {
1512                   printf(" unsigned, skipped\n");
1513                   fclose(fp);
1514                   break;
1515                 }
1516               if (sigfp)
1517                 {
1518                   if (!sigpool)
1519                     sigpool = read_sigs();
1520                   if (!checksig(sigpool, fp, sigfp))
1521                     {
1522                       printf(" checksig failed, skipped\n");
1523                       fclose(sigfp);
1524                       fclose(fp);
1525                       break;
1526                     }
1527                   fclose(sigfp);
1528                 }
1529             }
1530           repo_add_content(repo, fp, 0);
1531           fclose(fp);
1532           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1533           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1534           if (!descrdir)
1535             descrdir = "suse/setup/descr";
1536           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1537           if (!filename)
1538             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1539           if (!filename)
1540             {
1541               printf(" no packages file entry, skipped\n");
1542               break;
1543             }
1544           printf(" fetching\n");
1545           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1546             break;      /* hopeless */
1547           repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE);
1548           fclose(fp);
1549           /* add default language */
1550           filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
1551           if (!filename)
1552             filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
1553           if (filename)
1554             {
1555               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1556                 {
1557                   repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES);
1558                   fclose(fp);
1559                 }
1560             }
1561           repo_internalize(repo);
1562           data = repo_add_repodata(repo, 0);
1563           susetags_add_ext(repo, data);
1564           repodata_internalize(data);
1565           if (!badchecksum)
1566             writecachedrepo(repo, 0, 0, cinfo->cookie);
1567           repodata_create_stubs(repo_last_repodata(repo));
1568           break;
1569         default:
1570           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1571           repo_free(repo, 1);
1572           cinfo->repo = 0;
1573           break;
1574         }
1575     }
1576   if (sigpool)
1577     pool_free(sigpool);
1578 }
1579
1580
1581 int
1582 str2archid(Pool *pool, char *arch)
1583 {
1584   Id id;
1585   if (!*arch)
1586     return 0;
1587   id = str2id(pool, arch, 0);
1588   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1589     return id;
1590   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1591     return 0;
1592   return id;
1593 }
1594
1595
1596 #define DEPGLOB_NAME     1
1597 #define DEPGLOB_DEP      2
1598 #define DEPGLOB_NAMEDEP  3
1599
1600 int
1601 depglob(Pool *pool, char *name, Queue *job, int what)
1602 {
1603   Id p, pp;
1604   Id id = str2id(pool, name, 0);
1605   int i, match = 0;
1606
1607   if (id)
1608     {
1609       FOR_PROVIDES(p, pp, id)
1610         {
1611           Solvable *s = pool->solvables + p;
1612           match = 1;
1613           if (s->name == id && (what & DEPGLOB_NAME) != 0)
1614             {
1615               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1616               return 1;
1617             }
1618         }
1619       if (match)
1620         {
1621           if (what == DEPGLOB_NAMEDEP)
1622             printf("[using capability match for '%s']\n", name);
1623           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1624           return 1;
1625         }
1626     }
1627
1628   if (strpbrk(name, "[*?") == 0)
1629     return 0;
1630
1631   if ((what & DEPGLOB_NAME) != 0)
1632     {
1633       /* looks like a name glob. hard work. */
1634       for (p = 1; p < pool->nsolvables; p++)
1635         {
1636           Solvable *s = pool->solvables + p;
1637           if (!s->repo || !pool_installable(pool, s))
1638             continue;
1639           id = s->name;
1640           if (fnmatch(name, id2str(pool, id), 0) == 0)
1641             {
1642               for (i = 0; i < job->count; i += 2)
1643                 if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1644                   break;
1645               if (i == job->count)
1646                 queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1647               match = 1;
1648             }
1649         }
1650       if (match)
1651         return 1;
1652     }
1653   if ((what & DEPGLOB_DEP))
1654     {
1655       /* looks like a dep glob. really hard work. */
1656       for (id = 1; id < pool->ss.nstrings; id++)
1657         {
1658           if (!pool->whatprovides[id])
1659             continue;
1660           if (fnmatch(name, id2str(pool, id), 0) == 0)
1661             {
1662               if (!match && what == DEPGLOB_NAMEDEP)
1663                 printf("[using capability match for '%s']\n", name);
1664               for (i = 0; i < job->count; i += 2)
1665                 if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1666                   break;
1667               if (i == job->count)
1668                 queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1669               match = 1;
1670             }
1671         }
1672     }
1673   if (match)
1674     return 1;
1675   return 0;
1676 }
1677
1678 void
1679 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1680 {
1681   int i, match;
1682   Id p, pp;
1683   for (i = 0; i < job->count; i += 2)
1684     {
1685       Id select = job->elements[i] & SOLVER_SELECTMASK;
1686       if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES)
1687         continue;
1688       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1689       if (flags == REL_ARCH)
1690         job->elements[i] |= SOLVER_SETARCH;
1691       if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && job->elements[i])
1692         {
1693           const char *evrstr = id2str(pool, evr);
1694           if (strchr(evrstr, '-'))
1695             job->elements[i] |= SOLVER_SETEVR;
1696           else
1697             job->elements[i] |= SOLVER_SETEV;
1698         }
1699       /* make sure we still have matches */
1700       match = 0;
1701       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1702         {
1703           match = 1;
1704           break;
1705         }
1706       if (!match)
1707         {
1708           fprintf(stderr, "nothing matches %s\n", solver_select2str(pool, job->elements[i] & SOLVER_SELECTMASK, job->elements[i + 1]));
1709           exit(1);
1710         }
1711     }
1712 }
1713
1714 void
1715 addrelation_arch(Pool *pool, Queue *job, int flags, char *evr)
1716 {
1717   char *r;
1718   Id archid;
1719   if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1720     {
1721       *r = 0;
1722       addrelation(pool, job, REL_ARCH, archid);
1723       addrelation(pool, job, flags, str2id(pool, evr, 1));
1724       *r = '.';
1725     }
1726   else
1727     addrelation(pool, job, flags, str2id(pool, evr, 1));
1728 }
1729
1730 int
1731 limitrepo(Pool *pool, Id repofilter, Queue *job)
1732 {
1733   Queue mq;
1734   Id p, pp;
1735   int matched = 0;
1736   int i;
1737   Solvable *s;
1738
1739   queue_init(&mq);
1740   for (i = 0; i < job->count; i += 2)
1741     {
1742       queue_empty(&mq);
1743       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1744         {
1745           s = pool_id2solvable(pool, p);
1746           if (s->repo && s->repo->repoid == repofilter)
1747              queue_push(&mq, p);
1748         }
1749       if (mq.count)
1750         {
1751           if (!matched && i)
1752             {
1753               queue_deleten(job, 0, i);
1754               i = 0;
1755             }
1756           matched = 1;
1757           /* here we assume that repo == vendor, so we also set SOLVER_SETVENDOR */
1758           if (mq.count == 1)
1759             {
1760               job->elements[i] = SOLVER_SOLVABLE | (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO | SOLVER_NOAUTOSET;
1761               job->elements[i + 1] = mq.elements[0];
1762             }
1763           else
1764             {
1765               job->elements[i] = SOLVER_SOLVABLE_ONE_OF | (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO;
1766               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1767             }
1768         }
1769       else if (matched)
1770         {
1771           queue_deleten(job, i, 2);
1772           i -= 2;
1773         }
1774     }
1775   queue_free(&mq);
1776   return matched;
1777 }
1778
1779 void
1780 mkselect(Pool *pool, int mode, char *name, Queue *job)
1781 {
1782   char *r, *r2;
1783   Id archid;
1784
1785   if (*name == '/')
1786     {
1787       Dataiterator di;
1788       Queue q;
1789
1790       queue_init(&q);
1791       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1792       while (dataiterator_step(&di))
1793         {
1794           Solvable *s = pool->solvables + di.solvid;
1795           if (!s->repo || !pool_installable(pool, s))
1796             continue;
1797           queue_push(&q, di.solvid);
1798           dataiterator_skip_solvable(&di);
1799         }
1800       dataiterator_free(&di);
1801       if (q.count)
1802         {
1803           printf("[using file list match for '%s']\n", name);
1804           if (q.count > 1)
1805             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1806           else
1807             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1808           queue_free(&q);
1809           return;
1810         }
1811     }
1812   if ((r = strpbrk(name, "<=>")) != 0)
1813     {
1814       /* relation case, support:
1815        * depglob rel
1816        * depglob.arch rel
1817        */
1818       int rflags = 0;
1819       int nend = r - name;
1820       for (; *r; r++)
1821         {
1822           if (*r == '<')
1823             rflags |= REL_LT;
1824           else if (*r == '=')
1825             rflags |= REL_EQ;
1826           else if (*r == '>')
1827             rflags |= REL_GT;
1828           else
1829             break;
1830         }
1831       while (*r && *r == ' ' && *r == '\t')
1832         r++;
1833       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1834         nend--;
1835       name[nend] = 0;
1836       if (!*name || !*r)
1837         {
1838           fprintf(stderr, "bad relation\n");
1839           exit(1);
1840         }
1841       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
1842         {
1843           addrelation(pool, job, rflags, str2id(pool, r, 1));
1844           return;
1845         }
1846       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1847         {
1848           *r2 = 0;
1849           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
1850             {
1851               *r2 = '.';
1852               addrelation(pool, job, REL_ARCH, archid);
1853               addrelation(pool, job, rflags, str2id(pool, r, 1));
1854               return;
1855             }
1856           *r2 = '.';
1857         }
1858     }
1859   else
1860     {
1861       /* no relation case, support:
1862        * depglob
1863        * depglob.arch
1864        * nameglob-version
1865        * nameglob-version.arch
1866        * nameglob-version-release
1867        * nameglob-version-release.arch
1868        */
1869       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
1870         return;
1871       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1872         {
1873           *r = 0;
1874           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
1875             {
1876               *r = '.';
1877               addrelation(pool, job, REL_ARCH, archid);
1878               return;
1879             }
1880           *r = '.';
1881         }
1882       if ((r = strrchr(name, '-')) != 0)
1883         {
1884           *r = 0;
1885           if (depglob(pool, name, job, DEPGLOB_NAME))
1886             {
1887               /* have just the version */
1888               addrelation_arch(pool, job, REL_EQ, r + 1);
1889               *r = '-';
1890               return;
1891             }
1892           if ((r2 = strrchr(name, '-')) != 0)
1893             {
1894               *r = '-';
1895               *r2 = 0;
1896               r = r2;
1897               if (depglob(pool, name, job, DEPGLOB_NAME))
1898                 {
1899                   /* have version-release */
1900                   addrelation_arch(pool, job, REL_EQ, r + 1);
1901                   *r = '-';
1902                   return;
1903                 }
1904             }
1905           *r = '-';
1906         }
1907     }
1908   fprintf(stderr, "nothing matches '%s'\n", name);
1909   exit(1);
1910 }
1911
1912
1913 int
1914 yesno(const char *str)
1915 {
1916   char inbuf[128], *ip;
1917
1918   for (;;)
1919     {
1920       printf("%s", str);
1921       fflush(stdout);
1922       *inbuf = 0;
1923       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1924         {
1925           printf("Abort.\n");
1926           exit(1);
1927         }
1928       while (*ip == ' ' || *ip == '\t')
1929         ip++;
1930       if (*ip == 'q')
1931         {
1932           printf("Abort.\n");
1933           exit(1);
1934         }
1935       if (*ip == 'y' || *ip == 'n')
1936         return *ip == 'y' ? 1 : 0;
1937     }
1938 }
1939
1940 struct fcstate {
1941   FILE **newpkgsfps;
1942   Queue *checkq;
1943   int newpkgscnt;
1944   void *rpmdbstate;
1945 };
1946
1947 static void *
1948 fileconflict_cb(Pool *pool, Id p, void *cbdata)
1949 {
1950   struct fcstate *fcstate = cbdata;
1951   Solvable *s;
1952   Id rpmdbid;
1953   int i;
1954   FILE *fp;
1955
1956   if (!p)
1957     {
1958       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1959       return 0;
1960     }
1961   s = pool_id2solvable(pool, p);
1962   if (pool->installed && s->repo == pool->installed)
1963     {
1964       if (!s->repo->rpmdbid)
1965         return 0;
1966       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1967       if (!rpmdbid)
1968         return 0;
1969        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1970     }
1971   for (i = 0; i < fcstate->newpkgscnt; i++)
1972     if (fcstate->checkq->elements[i] == p)
1973       break;
1974   if (i == fcstate->newpkgscnt)
1975     return 0;
1976   fp = fcstate->newpkgsfps[i];
1977   if (!fp)
1978     return 0;
1979   rewind(fp);
1980   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1981 }
1982
1983 void
1984 runrpm(const char *arg, const char *name, int dupfd3)
1985 {
1986   pid_t pid;
1987   int status;
1988
1989   if ((pid = fork()) == (pid_t)-1)
1990     {
1991       perror("fork");
1992       exit(1);
1993     }
1994   if (pid == 0)
1995     {
1996       if (dupfd3 != -1 && dupfd3 != 3)
1997         {
1998           dup2(dupfd3, 3);
1999           close(dupfd3);
2000         }
2001       if (dupfd3 != -1)
2002         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2003       if (strcmp(arg, "-e") == 0)
2004         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2005       else
2006         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2007       perror("rpm");
2008       _exit(0);
2009     }
2010   while (waitpid(pid, &status, 0) != pid)
2011     ;
2012   if (status)
2013     {
2014       printf("rpm failed\n");
2015       exit(1);
2016     }
2017 }
2018
2019 static Id
2020 nscallback(Pool *pool, void *data, Id name, Id evr)
2021 {
2022   if (name == NAMESPACE_PRODUCTBUDDY)
2023     {    
2024       /* SUSE specific hack: each product has an associated rpm */
2025       Solvable *s = pool->solvables + evr; 
2026       Id p, pp, cap; 
2027       
2028       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
2029       if (!cap)
2030         return 0;
2031       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
2032       if (!cap)
2033         return 0;
2034       FOR_PROVIDES(p, pp, cap) 
2035         {
2036           Solvable *ps = pool->solvables + p; 
2037           if (ps->repo == s->repo && ps->arch == s->arch)
2038             break;
2039         }
2040       return p;
2041     }
2042   return 0;
2043 }
2044
2045 #ifdef SOFTLOCKS_PATH
2046
2047 void
2048 addsoftlocks(Pool *pool, Queue *job)
2049 {
2050   FILE *fp;
2051   Id type, id, p, pp;
2052   char *bp, *ep, buf[4096];
2053
2054   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2055     return;
2056   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2057     {
2058       while (*bp == ' ' || *bp == '\t')
2059         bp++;
2060       if (!*bp || *bp == '#')
2061         continue;
2062       for (ep = bp; *ep; ep++)
2063         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2064           break;
2065       *ep = 0;
2066       type = SOLVER_SOLVABLE_NAME;
2067       if (!strncmp(bp, "provides:", 9) && bp[9])
2068         {
2069           type = SOLVER_SOLVABLE_PROVIDES;
2070           bp += 9;
2071         }
2072       id = str2id(pool, bp, 1);
2073       if (pool->installed)
2074         {
2075           FOR_JOB_SELECT(p, pp, type, id)
2076             if (pool->solvables[p].repo == pool->installed)
2077               break;
2078           if (p)
2079             continue;   /* ignore, as it is already installed */
2080         }
2081       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2082     }
2083   fclose(fp);
2084 }
2085
2086 #endif
2087
2088
2089 void
2090 rewrite_repos(Pool *pool, Id *addedfileprovides)
2091 {
2092   Repo *repo;
2093   Repodata *data;
2094   Map providedids;
2095   Queue fileprovidesq;
2096   Id id;
2097   int i, j, n, nprovidedids;
2098   struct repoinfo *cinfo;
2099
2100   map_init(&providedids, pool->ss.nstrings);
2101   queue_init(&fileprovidesq);
2102   for (nprovidedids = 0; (id = addedfileprovides[nprovidedids]) != 0; nprovidedids++)
2103     MAPSET(&providedids, id);
2104   FOR_REPOS(i, repo)
2105     {
2106       /* make sure this repo has just one main repodata */
2107       if (!repo->nrepodata)
2108         continue;
2109       cinfo = repo->appdata;
2110       data = repo->repodata + 0;
2111       if (data->store.pagefd == -1)
2112         continue;
2113       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2114         {
2115           n = 0;
2116           for (j = 0; j < fileprovidesq.count; j++)
2117             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2118               n++;
2119           if (n == nprovidedids)
2120             continue;   /* nothing new added */
2121         }
2122       /* oh my! */
2123       for (j = 0; addedfileprovides[j]; j++)
2124         repodata_add_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[j]);
2125       repodata_internalize(data);
2126       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2127     }
2128   queue_free(&fileprovidesq);
2129   map_free(&providedids);
2130 }
2131
2132 #define MODE_LIST        0
2133 #define MODE_INSTALL     1
2134 #define MODE_ERASE       2
2135 #define MODE_UPDATE      3
2136 #define MODE_DISTUPGRADE 4
2137 #define MODE_VERIFY      5
2138 #define MODE_PATCH       6
2139 #define MODE_INFO        7
2140 #define MODE_REPOLIST    8
2141 #define MODE_SEARCH      9
2142 #define MODE_ERASECLEAN  10
2143
2144 void
2145 usage(int r)
2146 {
2147   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2148   fprintf(stderr, "\n");
2149   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2150   fprintf(stderr, "                  versions from the repositories\n");
2151   fprintf(stderr, "    erase:        erase installed packages\n");
2152   fprintf(stderr, "    info:         display package information\n");
2153   fprintf(stderr, "    install:      install packages\n");
2154   fprintf(stderr, "    list:         list packages\n");
2155   fprintf(stderr, "    repos:        list enabled repositories\n");
2156   fprintf(stderr, "    search:       search name/summary/description\n");
2157   fprintf(stderr, "    update:       update installed packages\n");
2158   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2159   fprintf(stderr, "\n");
2160   exit(r);
2161 }
2162
2163 int
2164 main(int argc, char **argv)
2165 {
2166   Pool *pool;
2167   Repo *commandlinerepo = 0;
2168   Id *commandlinepkgs = 0;
2169   Id p, pp;
2170   struct repoinfo *repoinfos;
2171   int nrepoinfos = 0;
2172   int mainmode = 0, mode = 0;
2173   int i, newpkgs;
2174   Queue job, checkq;
2175   Solver *solv = 0;
2176   Transaction *trans;
2177   char inbuf[128], *ip;
2178   int allpkgs = 0;
2179   FILE **newpkgsfps;
2180   struct fcstate fcstate;
2181   Id *addedfileprovides = 0;
2182   Id repofilter = 0;
2183
2184   argc--;
2185   argv++;
2186   if (!argv[0])
2187     usage(1);
2188   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2189     {
2190       mainmode = MODE_INSTALL;
2191       mode = SOLVER_INSTALL;
2192     }
2193   else if (!strcmp(argv[0], "patch"))
2194     {
2195       mainmode = MODE_PATCH;
2196       mode = SOLVER_INSTALL;
2197     }
2198   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2199     {
2200       mainmode = MODE_ERASE;
2201       mode = SOLVER_ERASE;
2202     }
2203   else if (!strcmp(argv[0], "eraseclean") || !strcmp(argv[0], "rmclean"))
2204     {
2205       mainmode = MODE_ERASECLEAN;
2206       mode = SOLVER_ERASE;
2207     }
2208   else if (!strcmp(argv[0], "list"))
2209     {
2210       mainmode = MODE_LIST;
2211       mode = 0;
2212     }
2213   else if (!strcmp(argv[0], "info"))
2214     {
2215       mainmode = MODE_INFO;
2216       mode = 0;
2217     }
2218   else if (!strcmp(argv[0], "search"))
2219     {
2220       mainmode = MODE_SEARCH;
2221       mode = 0;
2222     }
2223   else if (!strcmp(argv[0], "verify"))
2224     {
2225       mainmode = MODE_VERIFY;
2226       mode = SOLVER_VERIFY;
2227     }
2228   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2229     {
2230       mainmode = MODE_UPDATE;
2231       mode = SOLVER_UPDATE;
2232     }
2233   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2234     {
2235       mainmode = MODE_DISTUPGRADE;
2236       mode = SOLVER_UPDATE;
2237     }
2238   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2239     {
2240       mainmode = MODE_REPOLIST;
2241       mode = 0;
2242     }
2243   else
2244     usage(1);
2245
2246   pool = pool_create();
2247
2248 #if 0
2249   {
2250     const char *langs[] = {"de_DE", "de", "en"};
2251     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
2252   }
2253 #endif
2254
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 #if 1
2436                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
2437 #else
2438                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
2439 #endif
2440                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2441                   if (sum)
2442                     printf("    %s\n", sum);
2443                 }
2444             }
2445         }
2446       queue_free(&job);
2447       pool_free(pool);
2448       free_repoinfos(repoinfos, nrepoinfos);
2449       sat_free(commandlinepkgs);
2450 #ifdef FEDORA
2451       yum_substitute(pool, 0);
2452 #endif
2453       exit(0);
2454     }
2455
2456   if (mainmode == MODE_PATCH)
2457     {
2458       int pruneyou = 0;
2459       Map installedmap;
2460       Solvable *s;
2461
2462       map_init(&installedmap, pool->nsolvables);
2463       if (pool->installed)
2464         FOR_REPO_SOLVABLES(pool->installed, p, s)
2465           MAPSET(&installedmap, p);
2466
2467       /* install all patches */
2468       for (p = 1; p < pool->nsolvables; p++)
2469         {
2470           const char *type;
2471           int r;
2472           Id p2;
2473
2474           s = pool->solvables + p;
2475           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2476             continue;
2477           FOR_PROVIDES(p2, pp, s->name)
2478             {
2479               Solvable *s2 = pool->solvables + p2;
2480               if (s2->name != s->name)
2481                 continue;
2482               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2483               if (r < 0 || (r == 0 && p > p2))
2484                 break;
2485             }
2486           if (p2)
2487             continue;
2488           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2489           if (type && !strcmp(type, "optional"))
2490             continue;
2491           r = solvable_trivial_installable_map(s, &installedmap, 0);
2492           if (r == -1)
2493             continue;
2494           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2495             {
2496               if (!pruneyou++)
2497                 queue_empty(&job);
2498             }
2499           else if (pruneyou)
2500             continue;
2501           queue_push2(&job, SOLVER_SOLVABLE, p);
2502         }
2503       map_free(&installedmap);
2504     }
2505
2506   // add mode
2507   for (i = 0; i < job.count; i += 2)
2508     {
2509       if (mode == SOLVER_UPDATE)
2510         {
2511           /* make update of not installed packages an install */
2512           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2513             if (pool->installed && pool->solvables[p].repo == pool->installed)
2514               break;
2515           if (!p)
2516             {
2517               job.elements[i] |= SOLVER_INSTALL;
2518               continue;
2519             }
2520         }
2521       job.elements[i] |= mode;
2522       if (mainmode == MODE_ERASECLEAN)
2523         job.elements[i] |= SOLVER_CLEANDEPS;
2524     }
2525
2526   if (mainmode == MODE_DISTUPGRADE && allpkgs && repofilter)
2527     queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter);
2528
2529   // multiversion test
2530   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2531   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2532   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2533
2534 #ifdef SOFTLOCKS_PATH
2535   addsoftlocks(pool, &job);
2536 #endif
2537
2538 rerunsolver:
2539   for (;;)
2540     {
2541       Id problem, solution;
2542       int pcnt, scnt;
2543
2544       solv = solver_create(pool);
2545       solv->ignorealreadyrecommended = 1;
2546       solv->updatesystem = allpkgs && !repofilter && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
2547       solv->dosplitprovides = solv->updatesystem;
2548       solv->fixsystem = allpkgs && !repofilter && mainmode == MODE_VERIFY;
2549       if (mainmode == MODE_DISTUPGRADE && allpkgs && !repofilter)
2550         {
2551           solv->distupgrade = 1;
2552           solv->allowdowngrade = 1;
2553           solv->allowarchchange = 1;
2554           solv->allowvendorchange = 1;
2555         }
2556       if (mainmode == MODE_ERASE || mainmode == MODE_ERASECLEAN)
2557         solv->allowuninstall = 1;       /* don't nag */
2558
2559       solver_solve(solv, &job);
2560       if (!solv->problems.count)
2561         break;
2562       pcnt = solver_problem_count(solv);
2563       printf("Found %d problems:\n", pcnt);
2564       for (problem = 1; problem <= pcnt; problem++)
2565         {
2566           int take = 0;
2567           printf("Problem %d:\n", problem);
2568           solver_printprobleminfo(solv, problem);
2569           printf("\n");
2570           scnt = solver_solution_count(solv, problem);
2571           for (solution = 1; solution <= scnt; solution++)
2572             {
2573               printf("Solution %d:\n", solution);
2574               solver_printsolution(solv, problem, solution);
2575               printf("\n");
2576             }
2577           for (;;)
2578             {
2579               printf("Please choose a solution: ");
2580               fflush(stdout);
2581               *inbuf = 0;
2582               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2583                 {
2584                   printf("Abort.\n");
2585                   exit(1);
2586                 }
2587               while (*ip == ' ' || *ip == '\t')
2588                 ip++;
2589               if (*ip >= '0' && *ip <= '9')
2590                 {
2591                   take = atoi(ip);
2592                   if (take >= 1 && take <= scnt)
2593                     break;
2594                 }
2595               if (*ip == 's')
2596                 {
2597                   take = 0;
2598                   break;
2599                 }
2600               if (*ip == 'q')
2601                 {
2602                   printf("Abort.\n");
2603                   exit(1);
2604                 }
2605             }
2606           if (!take)
2607             continue;
2608           solver_take_solution(solv, problem, take, &job);
2609         }
2610       solver_free(solv);
2611       solv = 0;
2612     }
2613
2614   trans = &solv->trans;
2615   if (!trans->steps.count)
2616     {
2617       printf("Nothing to do.\n");
2618       exit(1);
2619     }
2620   printf("\n");
2621   printf("Transaction summary:\n\n");
2622   solver_printtransaction(solv);
2623
2624 #ifndef FEDORA
2625   if (1)
2626     {
2627       DUChanges duc[4];
2628       int i;
2629
2630       duc[0].path = "/";
2631       duc[1].path = "/usr/share/man";
2632       duc[2].path = "/sbin";
2633       duc[3].path = "/etc";
2634       transaction_calc_duchanges(trans, duc, 4);
2635       for (i = 0; i < 4; i++)
2636         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2637     }
2638 #endif
2639   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
2640   printf("\n");
2641
2642   if (!yesno("OK to continue (y/n)? "))
2643     {
2644       printf("Abort.\n");
2645       solver_free(solv);
2646       queue_free(&job);
2647       pool_free(pool);
2648       free_repoinfos(repoinfos, nrepoinfos);
2649       sat_free(commandlinepkgs);
2650 #ifdef FEDORA
2651       yum_substitute(pool, 0);
2652 #endif
2653       exit(1);
2654     }
2655
2656   queue_init(&checkq);
2657   newpkgs = transaction_installedresult(trans, &checkq);
2658   newpkgsfps = 0;
2659
2660   if (newpkgs)
2661     {
2662       int downloadsize = 0;
2663       for (i = 0; i < newpkgs; i++)
2664         {
2665           Solvable *s;
2666
2667           p = checkq.elements[i];
2668           s = pool_id2solvable(pool, p);
2669           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
2670         }
2671       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
2672       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
2673       for (i = 0; i < newpkgs; i++)
2674         {
2675           unsigned int medianr;
2676           char *loc;
2677           Solvable *s;
2678           struct repoinfo *cinfo;
2679           const unsigned char *chksum;
2680           Id chksumtype;
2681           Dataiterator di;
2682
2683           p = checkq.elements[i];
2684           s = pool_id2solvable(pool, p);
2685           if (s->repo == commandlinerepo)
2686             {
2687               loc = solvable_get_location(s, &medianr);
2688               if (!(newpkgsfps[i] = fopen(loc, "r")))
2689                 {
2690                   perror(loc);
2691                   exit(1);
2692                 }
2693               putchar('.');
2694               continue;
2695             }
2696           cinfo = s->repo->appdata;
2697           if (!cinfo)
2698             {
2699               printf("%s: no repository information\n", s->repo->name);
2700               exit(1);
2701             }
2702           loc = solvable_get_location(s, &medianr);
2703           if (!loc)
2704              continue;
2705
2706           if (pool->installed && pool->installed->nsolvables)
2707             {
2708               /* try a delta first */
2709               char *matchname = strdup(id2str(pool, s->name));
2710               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2711               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2712               while (dataiterator_step(&di))
2713                 {
2714                   Id baseevr, op;
2715
2716                   dataiterator_setpos_parent(&di);
2717                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2718                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2719                     continue;
2720                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2721                   FOR_PROVIDES(op, pp, s->name)
2722                     {
2723                       Solvable *os = pool->solvables + op;
2724                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2725                         break;
2726                     }
2727                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2728                     {
2729                       /* base is installed, run sequence check */
2730                       const char *seqname;
2731                       const char *seqevr;
2732                       const char *seqnum;
2733                       const char *seq;
2734                       const char *dloc;
2735                       FILE *fp;
2736                       char cmd[128];
2737                       int newfd;
2738
2739                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2740                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2741                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2742                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2743                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2744 #ifdef FEDORA
2745                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
2746 #else
2747                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
2748 #endif
2749                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
2750                         continue;       /* didn't match */
2751                       /* looks good, download delta */
2752                       chksumtype = 0;
2753                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2754                       if (!chksumtype)
2755                         continue;       /* no way! */
2756                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2757                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2758                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2759                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2760                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2761                         continue;
2762                       /* got it, now reconstruct */
2763                       newfd = opentmpfile();
2764 #ifdef FEDORA
2765                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
2766 #else
2767                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2768 #endif
2769                       fcntl(fileno(fp), F_SETFD, 0);
2770                       if (system(cmd))
2771                         {
2772                           close(newfd);
2773                           fclose(fp);
2774                           continue;
2775                         }
2776                       lseek(newfd, 0, SEEK_SET);
2777                       chksumtype = 0;
2778                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2779                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2780                         {
2781                           close(newfd);
2782                           fclose(fp);
2783                           continue;
2784                         }
2785                       newpkgsfps[i] = fdopen(newfd, "r");
2786                       fclose(fp);
2787                       break;
2788                     }
2789                 }
2790               dataiterator_free(&di);
2791               sat_free(matchname);
2792             }
2793           
2794           if (newpkgsfps[i])
2795             {
2796               putchar('d');
2797               fflush(stdout);
2798               continue;         /* delta worked! */
2799             }
2800           if (cinfo->type == TYPE_SUSETAGS)
2801             {
2802               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2803               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2804             }
2805           chksumtype = 0;
2806           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2807           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
2808             {
2809               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2810               exit(1);
2811             }
2812           putchar('.');
2813           fflush(stdout);
2814         }
2815       putchar('\n');
2816     }
2817
2818   if (newpkgs)
2819     {
2820       Queue conflicts;
2821
2822       printf("Searching for file conflicts\n");
2823       queue_init(&conflicts);
2824       fcstate.rpmdbstate = 0;
2825       fcstate.newpkgscnt = newpkgs;
2826       fcstate.checkq = &checkq;
2827       fcstate.newpkgsfps = newpkgsfps;
2828       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
2829       if (conflicts.count)
2830         {
2831           printf("\n");
2832           for (i = 0; i < conflicts.count; i += 5)
2833             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]));
2834           printf("\n");
2835           if (yesno("Re-run solver (y/n/q)? "))
2836             {
2837               for (i = 0; i < newpkgs; i++)
2838                 if (newpkgsfps[i])
2839                   fclose(newpkgsfps[i]);
2840               newpkgsfps = sat_free(newpkgsfps);
2841               solver_free(solv);
2842               pool_add_fileconflicts_deps(pool, &conflicts);
2843               pool_createwhatprovides(pool);    /* Hmm... */
2844               goto rerunsolver;
2845             }
2846         }
2847       queue_free(&conflicts);
2848     }
2849
2850   printf("Committing transaction:\n\n");
2851   transaction_order(trans, 0);
2852   for (i = 0; i < trans->steps.count; i++)
2853     {
2854       const char *evr, *evrp, *nvra;
2855       Solvable *s;
2856       int j;
2857       FILE *fp;
2858
2859       p = trans->steps.elements[i];
2860       s = pool_id2solvable(pool, p);
2861       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2862       switch(type)
2863         {
2864         case SOLVER_TRANSACTION_ERASE:
2865           printf("erase %s\n", solvid2str(pool, p));
2866           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2867             continue;
2868           /* strip epoch from evr */
2869           evr = evrp = id2str(pool, s->evr);
2870           while (*evrp >= '0' && *evrp <= '9')
2871             evrp++;
2872           if (evrp > evr && evrp[0] == ':' && evrp[1])
2873             evr = evrp + 1;
2874           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2875           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2876           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2877           break;
2878         case SOLVER_TRANSACTION_INSTALL:
2879         case SOLVER_TRANSACTION_MULTIINSTALL:
2880           printf("install %s\n", solvid2str(pool, p));
2881           for (j = 0; j < newpkgs; j++)
2882             if (checkq.elements[j] == p)
2883               break;
2884           fp = j < newpkgs ? newpkgsfps[j] : 0;
2885           if (!fp)
2886             continue;
2887           rewind(fp);
2888           lseek(fileno(fp), 0, SEEK_SET);
2889           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2890           fclose(fp);
2891           newpkgsfps[j] = 0;
2892           break;
2893         default:
2894           break;
2895         }
2896     }
2897
2898   for (i = 0; i < newpkgs; i++)
2899     if (newpkgsfps[i])
2900       fclose(newpkgsfps[i]);
2901   sat_free(newpkgsfps);
2902   queue_free(&checkq);
2903   solver_free(solv);
2904   queue_free(&job);
2905   pool_free(pool);
2906   free_repoinfos(repoinfos, nrepoinfos);
2907   sat_free(commandlinepkgs);
2908 #ifdef FEDORA
2909   yum_substitute(pool, 0);
2910 #endif
2911   exit(0);
2912 }