Imported Upstream version 0.6.7
[platform/upstream/libsolv.git] / examples / solv.c
1 /*
2  * Copyright (c) 2009-2013, 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 it does:
11  * - understands globs for package names / dependencies
12  * - understands .arch suffix
13  * - installation of commandline packages
14  * - repository data caching
15  * - on demand loading of secondary repository data
16  * - gpg and checksum verification
17  * - file conflicts
18  * - deltarpm support
19  * - fastestmirror implementation
20  *
21  * things available in the library but missing from solv:
22  * - vendor policy loading
23  * - soft locks file handling
24  * - multi version handling
25  */
26
27 #define _GNU_SOURCE
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <fnmatch.h>
33 #include <unistd.h>
34 #include <zlib.h>
35 #include <fcntl.h>
36 #include <assert.h>
37 #include <sys/utsname.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <time.h>
41 #include <sys/time.h>
42 #include <sys/dir.h>
43 #include <sys/stat.h>
44
45 #include <sys/socket.h>
46 #include <netdb.h>
47 #include <poll.h>
48 #include <errno.h>
49
50 #include "pool.h"
51 #include "poolarch.h"
52 #include "repo.h"
53 #include "evr.h"
54 #include "policy.h"
55 #include "util.h"
56 #include "solver.h"
57 #include "solverdebug.h"
58 #include "chksum.h"
59 #include "repo_solv.h"
60 #include "selection.h"
61
62 #include "repo_write.h"
63 #ifdef ENABLE_RPMDB
64 #include "repo_rpmdb.h"
65 #include "pool_fileconflicts.h"
66 #endif
67 #ifdef ENABLE_PUBKEY
68 #include "repo_pubkey.h"
69 #endif
70 #ifdef ENABLE_DEBIAN
71 #include "repo_deb.h"
72 #endif
73 #ifdef ENABLE_RPMMD
74 #include "repo_rpmmd.h"
75 #include "repo_repomdxml.h"
76 #include "repo_updateinfoxml.h"
77 #include "repo_deltainfoxml.h"
78 #endif
79 #ifdef ENABLE_APPDATA
80 #include "repo_appdata.h"
81 #endif
82 #ifdef ENABLE_SUSEREPO
83 #include "repo_products.h"
84 #include "repo_susetags.h"
85 #include "repo_content.h"
86 #endif
87 #ifdef SUSE
88 #include "repo_autopattern.h"
89 #endif
90 #include "solv_xfopen.h"
91
92 #ifdef FEDORA
93 # define REPOINFO_PATH "/etc/yum.repos.d"
94 #endif
95 #ifdef SUSE
96 # define REPOINFO_PATH "/etc/zypp/repos.d"
97 # define PRODUCTS_PATH "/etc/products.d"
98 # define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
99 #endif
100 #ifdef ENABLE_APPDATA
101 # define APPDATA_PATH "/usr/share/appdata"
102 #endif
103
104 #define SOLVCACHE_PATH "/var/cache/solv"
105
106 #define METADATA_EXPIRE (60 * 15)
107
108 struct repoinfo {
109   Repo *repo;
110
111   char *alias;
112   char *name;
113   int enabled;
114   int autorefresh;
115   char *baseurl;
116   char *metalink;
117   char *mirrorlist;
118   char *path;
119   int type;
120   int pkgs_gpgcheck;
121   int repo_gpgcheck;
122   int priority;
123   int keeppackages;
124   int metadata_expire;
125   char **components;
126   int ncomponents;
127
128   unsigned char cookie[32];
129   unsigned char extcookie[32];
130   int incomplete;
131 };
132
133 #ifdef FEDORA
134 char *
135 yum_substitute(Pool *pool, char *line)
136 {
137   char *p, *p2;
138   static char *releaseevr;
139   static char *basearch;
140
141   if (!line)
142     {
143       solv_free(releaseevr);
144       releaseevr = 0;
145       solv_free(basearch);
146       basearch = 0;
147       return 0;
148     }
149   p = line;
150   while ((p2 = strchr(p, '$')) != 0)
151     {
152       if (!strncmp(p2, "$releasever", 11))
153         {
154           if (!releaseevr)
155             {
156               void *rpmstate;
157               Queue q;
158         
159               queue_init(&q);
160               rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
161               rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
162               if (q.count)
163                 {
164                   void *handle;
165                   char *p;
166                   handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
167                   releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
168                   if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
169                     *p = 0;
170                 }
171               rpm_state_free(rpmstate);
172               queue_free(&q);
173               if (!releaseevr)
174                 {
175                   fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
176                   exit(1);
177                 }
178             }
179           *p2 = 0;
180           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
181           p2 = p + (p2 - line);
182           line = p;
183           p = p2 + strlen(releaseevr);
184           continue;
185         }
186       if (!strncmp(p2, "$basearch", 9))
187         {
188           if (!basearch)
189             {
190               struct utsname un;
191               if (uname(&un))
192                 {
193                   perror("uname");
194                   exit(1);
195                 }
196               basearch = strdup(un.machine);
197               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
198                 basearch[1] = '3';
199             }
200           *p2 = 0;
201           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
202           p2 = p + (p2 - line);
203           line = p;
204           p = p2 + strlen(basearch);
205           continue;
206         }
207       p = p2 + 1;
208     }
209   return line;
210 }
211 #endif
212
213 #define TYPE_UNKNOWN    0
214 #define TYPE_SUSETAGS   1
215 #define TYPE_RPMMD      2
216 #define TYPE_PLAINDIR   3
217 #define TYPE_DEBIAN     4
218
219 static int
220 read_repoinfos_sort(const void *ap, const void *bp)
221 {
222   const struct repoinfo *a = ap;
223   const struct repoinfo *b = bp;
224   return strcmp(a->alias, b->alias);
225 }
226
227 #if defined(SUSE) || defined(FEDORA)
228
229 struct repoinfo *
230 read_repoinfos(Pool *pool, int *nrepoinfosp)
231 {
232   const char *reposdir = REPOINFO_PATH;
233   char buf[4096];
234   char buf2[4096], *kp, *vp, *kpe;
235   DIR *dir;
236   FILE *fp;
237   struct dirent *ent;
238   int l, rdlen;
239   struct repoinfo *repoinfos = 0, *cinfo;
240   int nrepoinfos = 0;
241
242   rdlen = strlen(reposdir);
243   dir = opendir(reposdir);
244   if (!dir)
245     {
246       *nrepoinfosp = 0;
247       return 0;
248     }
249   while ((ent = readdir(dir)) != 0)
250     {
251       if (ent->d_name[0] == '.')
252         continue;
253       l = strlen(ent->d_name);
254       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
255         continue;
256       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
257       if ((fp = fopen(buf, "r")) == 0)
258         {
259           perror(buf);
260           continue;
261         }
262       cinfo = 0;
263       while(fgets(buf2, sizeof(buf2), fp))
264         {
265           l = strlen(buf2);
266           if (l == 0)
267             continue;
268           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
269             buf2[--l] = 0;
270           kp = buf2;
271           while (*kp == ' ' || *kp == '\t')
272             kp++;
273           if (!*kp || *kp == '#')
274             continue;
275 #ifdef FEDORA
276           if (strchr(kp, '$'))
277             kp = yum_substitute(pool, kp);
278 #endif
279           if (*kp == '[')
280             {
281               vp = strrchr(kp, ']');
282               if (!vp)
283                 continue;
284               *vp = 0;
285               repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
286               cinfo = repoinfos + nrepoinfos++;
287               memset(cinfo, 0, sizeof(*cinfo));
288               cinfo->alias = strdup(kp + 1);
289               cinfo->type = TYPE_RPMMD;
290               cinfo->autorefresh = 1;
291               cinfo->priority = 99;
292 #ifndef FEDORA
293               cinfo->repo_gpgcheck = 1;
294 #endif
295               cinfo->metadata_expire = METADATA_EXPIRE;
296               continue;
297             }
298           if (!cinfo)
299             continue;
300           vp = strchr(kp, '=');
301           if (!vp)
302             continue;
303           for (kpe = vp - 1; kpe >= kp; kpe--)
304             if (*kpe != ' ' && *kpe != '\t')
305               break;
306           if (kpe == kp)
307             continue;
308           vp++;
309           while (*vp == ' ' || *vp == '\t')
310             vp++;
311           kpe[1] = 0;
312           if (!strcmp(kp, "name"))
313             cinfo->name = strdup(vp);
314           else if (!strcmp(kp, "enabled"))
315             cinfo->enabled = *vp == '0' ? 0 : 1;
316           else if (!strcmp(kp, "autorefresh"))
317             cinfo->autorefresh = *vp == '0' ? 0 : 1;
318           else if (!strcmp(kp, "gpgcheck"))
319             cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
320           else if (!strcmp(kp, "repo_gpgcheck"))
321             cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
322           else if (!strcmp(kp, "baseurl"))
323             cinfo->baseurl = strdup(vp);
324           else if (!strcmp(kp, "mirrorlist"))
325             {
326               if (strstr(vp, "metalink"))
327                 cinfo->metalink = strdup(vp);
328               else
329                 cinfo->mirrorlist = strdup(vp);
330             }
331           else if (!strcmp(kp, "path"))
332             {
333               if (vp && strcmp(vp, "/") != 0)
334                 cinfo->path = strdup(vp);
335             }
336           else if (!strcmp(kp, "type"))
337             {
338               if (!strcmp(vp, "yast2"))
339                 cinfo->type = TYPE_SUSETAGS;
340               else if (!strcmp(vp, "rpm-md"))
341                 cinfo->type = TYPE_RPMMD;
342               else if (!strcmp(vp, "plaindir"))
343                 cinfo->type = TYPE_PLAINDIR;
344               else
345                 cinfo->type = TYPE_UNKNOWN;
346             }
347           else if (!strcmp(kp, "priority"))
348             cinfo->priority = atoi(vp);
349           else if (!strcmp(kp, "keeppackages"))
350             cinfo->keeppackages = *vp == '0' ? 0 : 1;
351         }
352       fclose(fp);
353       cinfo = 0;
354     }
355   closedir(dir);
356   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
357   *nrepoinfosp = nrepoinfos;
358   return repoinfos;
359 }
360
361 #endif
362
363 #ifdef DEBIAN
364
365 struct repoinfo *
366 read_repoinfos(Pool *pool, int *nrepoinfosp)
367 {
368   FILE *fp;
369   char buf[4096];
370   char buf2[4096];
371   int l;
372   char *kp, *url, *distro;
373   struct repoinfo *repoinfos = 0, *cinfo;
374   int nrepoinfos = 0;
375   DIR *dir = 0;
376   struct dirent *ent;
377
378   fp = fopen("/etc/apt/sources.list", "r");
379   while (1)
380     {
381       if (!fp)
382         {
383           if (!dir)
384             {
385               dir = opendir("/etc/apt/sources.list.d");
386               if (!dir)
387                 break;
388             }
389           if ((ent = readdir(dir)) == 0)
390             {
391               closedir(dir);
392               break;
393             }
394           if (ent->d_name[0] == '.')
395             continue;
396           l = strlen(ent->d_name);
397           if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
398             continue;
399           snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
400           if (!(fp = fopen(buf, "r")))
401             continue;
402         }
403       while(fgets(buf2, sizeof(buf2), fp))
404         {
405           l = strlen(buf2);
406           if (l == 0)
407             continue;
408           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
409             buf2[--l] = 0;
410           kp = buf2;
411           while (*kp == ' ' || *kp == '\t')
412             kp++;
413           if (!*kp || *kp == '#')
414             continue;
415           if (strncmp(kp, "deb", 3) != 0)
416             continue;
417           kp += 3;
418           if (*kp != ' ' && *kp != '\t')
419             continue;
420           while (*kp == ' ' || *kp == '\t')
421             kp++;
422           if (!*kp)
423             continue;
424           url = kp;
425           while (*kp && *kp != ' ' && *kp != '\t')
426             kp++;
427           if (*kp)
428             *kp++ = 0;
429           while (*kp == ' ' || *kp == '\t')
430             kp++;
431           if (!*kp)
432             continue;
433           distro = kp;
434           while (*kp && *kp != ' ' && *kp != '\t')
435             kp++;
436           if (*kp)
437             *kp++ = 0;
438           while (*kp == ' ' || *kp == '\t')
439             kp++;
440           if (!*kp)
441             continue;
442           repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
443           cinfo = repoinfos + nrepoinfos++;
444           memset(cinfo, 0, sizeof(*cinfo));
445           cinfo->baseurl = strdup(url);
446           cinfo->alias = solv_dupjoin(url, "/", distro);
447           cinfo->name = strdup(distro);
448           cinfo->type = TYPE_DEBIAN;
449           cinfo->enabled = 1;
450           cinfo->autorefresh = 1;
451           cinfo->repo_gpgcheck = 1;
452           cinfo->metadata_expire = METADATA_EXPIRE;
453           while (*kp)
454             {
455               char *compo;
456               while (*kp == ' ' || *kp == '\t')
457                 kp++;
458               if (!*kp)
459                 break;
460               compo = kp;
461               while (*kp && *kp != ' ' && *kp != '\t')
462                 kp++;
463               if (*kp)
464                 *kp++ = 0;
465               cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
466               cinfo->components[cinfo->ncomponents++] = strdup(compo);
467             }
468         }
469       fclose(fp);
470       fp = 0;
471     }
472   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
473   *nrepoinfosp = nrepoinfos;
474   return repoinfos;
475 }
476
477 #endif
478
479
480 void
481 free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
482 {
483   int i, j;
484   for (i = 0; i < nrepoinfos; i++)
485     {
486       struct repoinfo *cinfo = repoinfos + i;
487       solv_free(cinfo->name);
488       solv_free(cinfo->alias);
489       solv_free(cinfo->path);
490       solv_free(cinfo->metalink);
491       solv_free(cinfo->mirrorlist);
492       solv_free(cinfo->baseurl);
493       for (j = 0; j < cinfo->ncomponents; j++)
494         solv_free(cinfo->components[j]);
495       solv_free(cinfo->components);
496     }
497   solv_free(repoinfos);
498 }
499
500 static inline int
501 opentmpfile()
502 {
503   char tmpl[100];
504   int fd;
505
506   strcpy(tmpl, "/var/tmp/solvXXXXXX");
507   fd = mkstemp(tmpl);
508   if (fd < 0)
509     {
510       perror("mkstemp");
511       exit(1);
512     }
513   unlink(tmpl);
514   return fd;
515 }
516
517 static int
518 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
519 {
520   char buf[1024];
521   const unsigned char *sum;
522   Chksum *h;
523   int l;
524
525   h = solv_chksum_create(chksumtype);
526   if (!h)
527     {
528       printf("%s: unknown checksum type\n", file);
529       return 0;
530     }
531   while ((l = read(fd, buf, sizeof(buf))) > 0)
532     solv_chksum_add(h, buf, l);
533   lseek(fd, 0, SEEK_SET);
534   l = 0;
535   sum = solv_chksum_get(h, &l);
536   if (memcmp(sum, chksum, l))
537     {
538       printf("%s: checksum mismatch\n", file);
539       solv_chksum_free(h, 0);
540       return 0;
541     }
542   solv_chksum_free(h, 0);
543   return 1;
544 }
545
546 void
547 findfastest(char **urls, int nurls)
548 {
549   int i, j, port;
550   int *socks, qc;
551   struct pollfd *fds;
552   char *p, *p2, *q;
553   char portstr[16];
554   struct addrinfo hints, *result;;
555
556   fds = solv_calloc(nurls, sizeof(*fds));
557   socks = solv_calloc(nurls, sizeof(*socks));
558   for (i = 0; i < nurls; i++)
559     {
560       socks[i] = -1;
561       p = strchr(urls[i], '/');
562       if (!p)
563         continue;
564       if (p[1] != '/')
565         continue;
566       p += 2;
567       q = strchr(p, '/');
568       qc = 0;
569       if (q)
570         {
571           qc = *q;
572           *q = 0;
573         }
574       if ((p2 = strchr(p, '@')) != 0)
575         p = p2 + 1;
576       port = 80;
577       if (!strncmp("https:", urls[i], 6))
578         port = 443;
579       else if (!strncmp("ftp:", urls[i], 4))
580         port = 21;
581       if ((p2 = strrchr(p, ':')) != 0)
582         {
583           port = atoi(p2 + 1);
584           if (q)
585             *q = qc;
586           q = p2;
587           qc = *q;
588           *q = 0;
589         }
590       sprintf(portstr, "%d", port);
591       memset(&hints, 0, sizeof(struct addrinfo));
592       hints.ai_family = AF_UNSPEC;
593       hints.ai_socktype = SOCK_STREAM;
594       hints.ai_flags = AI_NUMERICSERV;
595       result = 0;
596       if (!getaddrinfo(p, portstr, &hints, &result))
597         {
598           socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
599           if (socks[i] >= 0)
600             {
601               fcntl(socks[i], F_SETFL, O_NONBLOCK);
602               if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
603                 {
604                   if (errno != EINPROGRESS)
605                     {
606                       close(socks[i]);
607                       socks[i] = -1;
608                     }
609                 }
610             }
611           freeaddrinfo(result);
612         }
613       if (q)
614         *q = qc;
615     }
616   for (;;)
617     {
618       for (i = j = 0; i < nurls; i++)
619         {
620           if (socks[i] < 0)
621             continue;
622           fds[j].fd = socks[i];
623           fds[j].events = POLLOUT;
624           j++;
625         }
626       if (j < 2)
627         {
628           i = j - 1;
629           break;
630         }
631       if (poll(fds, j, 10000) <= 0)
632         {
633           i = -1;       /* something is wrong */
634           break;
635         }
636       for (i = 0; i < j; i++)
637         if ((fds[i].revents & POLLOUT) != 0)
638           {
639             int soe = 0;
640             socklen_t soel = sizeof(int);
641             if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
642               {
643                 /* connect failed, kill socket */
644                 for (j = 0; j < nurls; j++)
645                   if (socks[j] == fds[i].fd)
646                     {
647                       close(socks[j]);
648                       socks[j] = -1;
649                     }
650                 i = j + 1;
651                 break;
652               }
653             break;      /* horray! */
654           }
655       if (i == j + 1)
656         continue;
657       if (i == j)
658         i = -1;         /* something is wrong, no bit was set */
659       break;
660     }
661   /* now i contains the fastest fd index */
662   if (i >= 0)
663     {
664       for (j = 0; j < nurls; j++)
665         if (socks[j] == fds[i].fd)
666           break;
667       if (j != 0)
668         {
669           char *url0 = urls[0];
670           urls[0] = urls[j];
671           urls[j] = url0;
672         }
673     }
674   for (i = j = 0; i < nurls; i++)
675     if (socks[i] >= 0)
676       close(socks[i]);
677   free(socks);
678   free(fds);
679 }
680
681 char *
682 findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
683 {
684   char buf[4096], *bp, *ep;
685   char **urls = 0;
686   int nurls = 0;
687   int i;
688
689   if (chksumtypep)
690     *chksumtypep = 0;
691   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
692     {
693       while (*bp == ' ' || *bp == '\t')
694         bp++;
695       if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
696         {
697           bp += 20;
698           if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
699             *chksumtypep = REPOKEY_TYPE_SHA256;
700           continue;
701         }
702       if (strncmp(bp, "<url", 4))
703         continue;
704       bp = strchr(bp, '>');
705       if (!bp)
706         continue;
707       bp++;
708       ep = strstr(bp, "repodata/repomd.xml</url>");
709       if (!ep)
710         continue;
711       *ep = 0;
712       if (strncmp(bp, "http", 4))
713         continue;
714       urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
715       urls[nurls++] = strdup(bp);
716     }
717   if (nurls)
718     {
719       if (nurls > 1)
720         findfastest(urls, nurls > 5 ? 5 : nurls);
721       bp = urls[0];
722       urls[0] = 0;
723       for (i = 0; i < nurls; i++)
724         solv_free(urls[i]);
725       solv_free(urls);
726       ep = strchr(bp, '/');
727       if ((ep = strchr(ep + 2, '/')) != 0)
728         {
729           *ep = 0;
730           printf("[using mirror %s]\n", bp);
731           *ep = '/';
732         }
733       return bp;
734     }
735   return 0;
736 }
737
738 char *
739 findmirrorlisturl(FILE *fp)
740 {
741   char buf[4096], *bp, *ep;
742   int i, l;
743   char **urls = 0;
744   int nurls = 0;
745
746   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
747     {
748       while (*bp == ' ' || *bp == '\t')
749         bp++;
750       if (!*bp || *bp == '#')
751         continue;
752       l = strlen(bp);
753       while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
754         bp[--l] = 0;
755       urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
756       urls[nurls++] = strdup(bp);
757     }
758   if (nurls)
759     {
760       if (nurls > 1)
761         findfastest(urls, nurls > 5 ? 5 : nurls);
762       bp = urls[0];
763       urls[0] = 0;
764       for (i = 0; i < nurls; i++)
765         solv_free(urls[i]);
766       solv_free(urls);
767       ep = strchr(bp, '/');
768       if ((ep = strchr(ep + 2, '/')) != 0)
769         {
770           *ep = 0;
771           printf("[using mirror %s]\n", bp);
772           *ep = '/';
773         }
774       return bp;
775     }
776   return 0;
777 }
778
779 static inline int
780 iscompressed(const char *name)
781 {
782   return solv_xfopen_iscompressed(name) != 0;
783 }
784
785 FILE *
786 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
787 {
788   FILE *fp;
789   pid_t pid;
790   int fd, l;
791   int status;
792   char url[4096];
793   const char *baseurl = cinfo->baseurl;
794
795   if (!baseurl)
796     {
797       if (!cinfo->metalink && !cinfo->mirrorlist)
798         return 0;
799       if (file != cinfo->metalink && file != cinfo->mirrorlist)
800         {
801           unsigned char mlchksum[32];
802           Id mlchksumtype;
803           fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
804           mlchksumtype = 0;
805           if (!fp)
806             return 0;
807           if (cinfo->metalink)
808             cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
809           else
810             cinfo->baseurl = findmirrorlisturl(fp);
811           fclose(fp);
812           if (!cinfo->baseurl)
813             return 0;
814 #ifdef FEDORA
815           if (strchr(cinfo->baseurl, '$'))
816             {
817               char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
818               free(cinfo->baseurl);
819               cinfo->baseurl = strdup(b);
820             }
821 #endif
822           if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
823             {
824               chksumtype = mlchksumtype;
825               chksum = mlchksum;
826             }
827           return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
828         }
829       snprintf(url, sizeof(url), "%s", file);
830     }
831   else
832     {
833       l = strlen(baseurl);
834       if (l && baseurl[l - 1] == '/')
835         snprintf(url, sizeof(url), "%s%s", baseurl, file);
836       else
837         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
838     }
839   fd = opentmpfile();
840   // printf("url: %s\n", url);
841   if ((pid = fork()) == (pid_t)-1)
842     {
843       perror("fork");
844       exit(1);
845     }
846   if (pid == 0)
847     {
848       if (fd != 1)
849         {
850           dup2(fd, 1);
851           close(fd);
852         }
853       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
854       perror("curl");
855       _exit(0);
856     }
857   status = 0;
858   while (waitpid(pid, &status, 0) != pid)
859     ;
860   if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
861     {
862       /* empty file */
863       close(fd);
864       return 0;
865     }
866   lseek(fd, 0, SEEK_SET);
867   if (status)
868     {
869       printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
870       if (markincomplete)
871         cinfo->incomplete = 1;
872       close(fd);
873       return 0;
874     }
875   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
876     {
877       if (markincomplete)
878         cinfo->incomplete = 1;
879       close(fd);
880       return 0;
881     }
882   fcntl(fd, F_SETFD, FD_CLOEXEC);
883   if (uncompress)
884     {
885       if (solv_xfopen_iscompressed(file) < 0)
886         {
887           printf("%s: unsupported compression\n", file);
888           if (markincomplete)
889             cinfo->incomplete = 1;
890           close(fd);
891           return 0;
892         }
893       fp = solv_xfopen_fd(file, fd, "r");
894     }
895   else
896     fp = fdopen(fd, "r");
897   if (!fp)
898     close(fd);
899   return fp;
900 }
901
902 #ifndef DEBIAN
903
904 static void
905 cleanupgpg(char *gpgdir)
906 {
907   char cmd[256];
908   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
909   unlink(cmd);
910   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
911   unlink(cmd);
912   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
913   unlink(cmd);
914   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
915   unlink(cmd);
916   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
917   unlink(cmd);
918   rmdir(gpgdir);
919 }
920
921 int
922 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
923 {
924   char *gpgdir;
925   char *keysfile;
926   const char *pubkey;
927   char cmd[256];
928   FILE *kfp;
929   Solvable *s;
930   Id p;
931   off_t posfp, possigfp;
932   int r, nkeys;
933
934   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
935   if (!gpgdir)
936     return 0;
937   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
938   if (!(kfp = fopen(keysfile, "w")) )
939     {
940       cleanupgpg(gpgdir);
941       return 0;
942     }
943   nkeys = 0;
944   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
945     {
946       if (!s->repo)
947         continue;
948       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
949       if (!pubkey || !*pubkey)
950         continue;
951       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
952         break;
953       if (fputc('\n', kfp) == EOF)      /* Just in case... */
954         break;
955       nkeys++;
956     }
957   if (fclose(kfp) || !nkeys)
958     {
959       cleanupgpg(gpgdir);
960       return 0;
961     }
962   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
963   if (system(cmd))
964     {
965       fprintf(stderr, "key import error\n");
966       cleanupgpg(gpgdir);
967       return 0;
968     }
969   unlink(keysfile);
970   posfp = lseek(fileno(fp), 0, SEEK_CUR);
971   lseek(fileno(fp), 0, SEEK_SET);
972   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
973   lseek(fileno(sigfp), 0, SEEK_SET);
974   snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
975   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
976   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
977   r = system(cmd);
978   lseek(fileno(sigfp), possigfp, SEEK_SET);
979   lseek(fileno(fp), posfp, SEEK_SET);
980   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
981   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
982   cleanupgpg(gpgdir);
983   return r == 0 ? 1 : 0;
984 }
985
986 #else
987
988 static int
989 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
990 {
991   char cmd[256];
992   int r;
993
994   snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
995   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
996   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
997   r = system(cmd);
998   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
999   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
1000   return r == 0 ? 1 : 0;
1001 }
1002
1003 #endif
1004
1005 static Pool *
1006 read_sigs()
1007 {
1008   Pool *sigpool = pool_create();
1009 #if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
1010   Repo *repo = repo_create(sigpool, "pubkeys");
1011   repo_add_rpmdb_pubkeys(repo, 0);
1012 #endif
1013   return sigpool;
1014 }
1015
1016 static int
1017 downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
1018 {
1019   FILE *sigfp;
1020   sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0);
1021   if (!sigfp)
1022     {
1023       printf(" unsigned, skipped\n");
1024       return 0;
1025     }
1026   if (!*sigpool)
1027     *sigpool = read_sigs();
1028   if (!checksig(*sigpool, fp, sigfp))
1029     {
1030       printf(" checksig failed, skipped\n");
1031       fclose(sigfp);
1032       return 0;
1033     }
1034   fclose(sigfp);
1035   return 1;
1036 }
1037
1038 #define CHKSUM_IDENT "1.1"
1039
1040 void
1041 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
1042 {
1043   char buf[4096];
1044   Chksum *h = solv_chksum_create(chktype);
1045   int l;
1046
1047   solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
1048   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
1049     solv_chksum_add(h, buf, l);
1050   rewind(fp);
1051   solv_chksum_free(h, out);
1052 }
1053
1054 void
1055 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
1056 {
1057   Chksum *h = solv_chksum_create(chktype);
1058   solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
1059   if (cookie)
1060     solv_chksum_add(h, cookie, 32);
1061   solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
1062   solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
1063   solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
1064   solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
1065   solv_chksum_free(h, out);
1066 }
1067
1068 void
1069 setarch(Pool *pool)
1070 {
1071   struct utsname un;
1072   if (uname(&un))
1073     {
1074       perror("uname");
1075       exit(1);
1076     }
1077   pool_setarch(pool, un.machine);
1078 }
1079
1080 char *userhome;
1081
1082 char *
1083 calccachepath(Repo *repo, const char *repoext, int forcesystemloc)
1084 {
1085   char *q, *p;
1086   int l;
1087   if (!forcesystemloc && userhome && getuid())
1088     p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
1089   else
1090     p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
1091   l = strlen(p);
1092   p = pool_tmpappend(repo->pool, p, repo->name, 0);
1093   if (repoext)
1094     {
1095       p = pool_tmpappend(repo->pool, p, "_", repoext);
1096       p = pool_tmpappend(repo->pool, p, ".solvx", 0);
1097     }
1098   else
1099     p = pool_tmpappend(repo->pool, p, ".solv", 0);
1100   q = p + l;
1101   if (*q == '.')
1102     *q = '_';
1103   for (; *q; q++)
1104     if (*q == '/')
1105       *q = '_';
1106   return p;
1107 }
1108
1109 int
1110 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
1111 {
1112   FILE *fp;
1113   unsigned char mycookie[32];
1114   unsigned char myextcookie[32];
1115   struct repoinfo *cinfo;
1116   int flags;
1117   int forcesystemloc;
1118
1119   forcesystemloc = mark & 2 ? 0 : 1;
1120   if (mark < 2 && userhome && getuid())
1121     {
1122       /* first try home location */
1123       int res = usecachedrepo(repo, repoext, cookie, mark | 2);
1124       if (res)
1125         return res;
1126     }
1127   mark &= 1;
1128   cinfo = repo->appdata;
1129   if (!(fp = fopen(calccachepath(repo, repoext, forcesystemloc), "r")))
1130     return 0;
1131   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
1132     {
1133       fclose(fp);
1134       return 0;
1135     }
1136   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
1137     {
1138       fclose(fp);
1139       return 0;
1140     }
1141   if (cinfo && !repoext)
1142     {
1143       if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
1144         {
1145           fclose(fp);
1146           return 0;
1147         }
1148     }
1149   rewind(fp);
1150
1151   flags = 0;
1152   if (repoext)
1153     {
1154       flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
1155       if (strcmp(repoext, "DL") != 0)
1156         flags |= REPO_LOCALPOOL;        /* no local pool for DL so that we can compare IDs */
1157     }
1158
1159   if (repo_add_solv(repo, fp, flags))
1160     {
1161       fclose(fp);
1162       return 0;
1163     }
1164   if (cinfo && !repoext)
1165     {
1166       memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
1167       memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
1168     }
1169   if (mark)
1170     futimens(fileno(fp), 0);    /* try to set modification time */
1171   fclose(fp);
1172   return 1;
1173 }
1174
1175 void
1176 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
1177 {
1178   FILE *fp;
1179   int i, fd;
1180   char *tmpl, *cachedir;
1181   struct repoinfo *cinfo;
1182   int onepiece;
1183
1184   cinfo = repo->appdata;
1185   if (cinfo && cinfo->incomplete)
1186     return;
1187   cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
1188   if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
1189     printf("[created %s]\n", cachedir);
1190   /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
1191   tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
1192   fd = mkstemp(tmpl);
1193   if (fd < 0)
1194     {
1195       free(tmpl);
1196       return;
1197     }
1198   fchmod(fd, 0444);
1199   if (!(fp = fdopen(fd, "w")))
1200     {
1201       close(fd);
1202       unlink(tmpl);
1203       free(tmpl);
1204       return;
1205     }
1206
1207   onepiece = 1;
1208   for (i = repo->start; i < repo->end; i++)
1209    if (repo->pool->solvables[i].repo != repo)
1210      break;
1211   if (i < repo->end)
1212     onepiece = 0;
1213
1214   if (!info)
1215     repo_write(repo, fp);
1216   else if (repoext)
1217     repodata_write(info, fp);
1218   else
1219     {
1220       int oldnrepodata = repo->nrepodata;
1221       repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;    /* XXX: do this right */
1222       repo_write(repo, fp);
1223       repo->nrepodata = oldnrepodata;
1224       onepiece = 0;
1225     }
1226
1227   if (!repoext && cinfo)
1228     {
1229       if (!cinfo->extcookie[0])
1230         {
1231           /* create the ext cookie and append it */
1232           /* we just need some unique ID */
1233           struct stat stb;
1234           if (!fstat(fileno(fp), &stb))
1235             memset(&stb, 0, sizeof(stb));
1236           calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie, cinfo->extcookie);
1237           if (cinfo->extcookie[0] == 0)
1238             cinfo->extcookie[0] = 1;
1239         }
1240       if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
1241         {
1242           fclose(fp);
1243           unlink(tmpl);
1244           free(tmpl);
1245           return;
1246         }
1247     }
1248   /* append our cookie describing the metadata state */
1249   if (fwrite(cookie, 32, 1, fp) != 1)
1250     {
1251       fclose(fp);
1252       unlink(tmpl);
1253       free(tmpl);
1254       return;
1255     }
1256   if (fclose(fp))
1257     {
1258       unlink(tmpl);
1259       free(tmpl);
1260       return;
1261     }
1262   if (onepiece)
1263     {
1264       /* switch to just saved repo to activate paging and save memory */
1265       FILE *fp = fopen(tmpl, "r");
1266       if (fp)
1267         {
1268           if (!repoext)
1269             {
1270               /* main repo */
1271               repo_empty(repo, 1);
1272               if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
1273                 {
1274                   /* oops, no way to recover from here */
1275                   fprintf(stderr, "internal error\n");
1276                   exit(1);
1277                 }
1278             }
1279           else
1280             {
1281               int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
1282               /* make sure repodata contains complete repo */
1283               /* (this is how repodata_write saves it) */
1284               repodata_extend_block(info, repo->start, repo->end - repo->start);
1285               info->state = REPODATA_LOADING;
1286               if (strcmp(repoext, "DL") != 0)
1287                 flags |= REPO_LOCALPOOL;
1288               repo_add_solv(repo, fp, flags);
1289               info->state = REPODATA_AVAILABLE; /* in case the load failed */
1290             }
1291           fclose(fp);
1292         }
1293     }
1294   if (!rename(tmpl, calccachepath(repo, repoext, 0)))
1295     unlink(tmpl);
1296   free(tmpl);
1297 }
1298
1299
1300 #ifdef ENABLE_RPMMD
1301 /* repomd helpers */
1302
1303 static inline const char *
1304 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1305 {
1306   Pool *pool = repo->pool;
1307   Dataiterator di;
1308   const char *filename;
1309
1310   filename = 0;
1311   *chksump = 0;
1312   *chksumtypep = 0;
1313   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1314   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1315   if (dataiterator_step(&di))
1316     {
1317       dataiterator_setpos_parent(&di);
1318       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1319       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
1320     }
1321   dataiterator_free(&di);
1322   if (filename && !*chksumtypep)
1323     {
1324       printf("no %s file checksum!\n", what);
1325       filename = 0;
1326     }
1327   return filename;
1328 }
1329
1330 int
1331 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
1332 {
1333   Id chksumtype, handle;
1334   const unsigned char *chksum;
1335   const char *filename;
1336
1337   filename = repomd_find(repo, what, &chksum, &chksumtype);
1338   if (!filename)
1339     return 0;
1340   if (!strcmp(what, "prestodelta"))
1341     what = "deltainfo";
1342   handle = repodata_new_handle(data);
1343   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1344   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1345   repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1346   if (!strcmp(what, "deltainfo"))
1347     {
1348       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1349       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1350     }
1351   if (!strcmp(what, "filelists"))
1352     {
1353       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1354       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1355     }
1356   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1357   return 1;
1358 }
1359
1360 int
1361 repomd_load_ext(Repo *repo, Repodata *data)
1362 {
1363   const char *filename, *repomdtype;
1364   char ext[3];
1365   FILE *fp;
1366   struct repoinfo *cinfo;
1367   const unsigned char *filechksum;
1368   Id filechksumtype;
1369   int r = 0;
1370
1371   cinfo = repo->appdata;
1372   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1373   if (!repomdtype)
1374     return 0;
1375   if (!strcmp(repomdtype, "filelists"))
1376     strcpy(ext, "FL");
1377   else if (!strcmp(repomdtype, "deltainfo"))
1378     strcpy(ext, "DL");
1379   else
1380     return 0;
1381   printf("[%s:%s", repo->name, ext);
1382   if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
1383     {
1384       printf(" cached]\n"); fflush(stdout);
1385       return 1;
1386     }
1387   printf(" fetching]\n"); fflush(stdout);
1388   filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1389   filechksumtype = 0;
1390   filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1391   if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1392     return 0;
1393   if (!strcmp(ext, "FL"))
1394     r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
1395   else if (!strcmp(ext, "DL"))
1396     r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
1397   fclose(fp);
1398   if (r)
1399     {
1400       printf("%s\n", pool_errstr(repo->pool));
1401       return 0;
1402     }
1403   writecachedrepo(repo, data, ext, cinfo->extcookie);
1404   return 1;
1405 }
1406
1407 #endif
1408
1409
1410 #ifdef ENABLE_SUSEREPO
1411 /* susetags helpers */
1412
1413 static inline const char *
1414 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1415 {
1416   Pool *pool = repo->pool;
1417   Dataiterator di;
1418   const char *filename;
1419
1420   filename = 0;
1421   *chksump = 0;
1422   *chksumtypep = 0;
1423   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1424   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1425   if (dataiterator_step(&di))
1426     {
1427       dataiterator_setpos_parent(&di);
1428       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1429       filename = what;
1430     }
1431   dataiterator_free(&di);
1432   if (filename && !*chksumtypep)
1433     {
1434       printf("no %s file checksum!\n", what);
1435       filename = 0;
1436     }
1437   return filename;
1438 }
1439
1440 static Id susetags_langtags[] = {
1441   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1442   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1443   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1444   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1445   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1446   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1447   0, 0
1448 };
1449
1450 void
1451 susetags_add_ext(Repo *repo, Repodata *data)
1452 {
1453   Pool *pool = repo->pool;
1454   Dataiterator di;
1455   char ext[3];
1456   Id handle, filechksumtype;
1457   const unsigned char *filechksum;
1458   int i;
1459
1460   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1461   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1462   while (dataiterator_step(&di))
1463     {
1464       if (strncmp(di.kv.str, "packages.", 9) != 0)
1465         continue;
1466       if (!strcmp(di.kv.str + 9, "gz"))
1467         continue;
1468       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1469         continue;
1470       ext[0] = di.kv.str[9];
1471       ext[1] = di.kv.str[10];
1472       ext[2] = 0;
1473       if (!strcmp(ext, "en"))
1474         continue;
1475       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1476         continue;
1477       handle = repodata_new_handle(data);
1478       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1479       if (filechksumtype)
1480         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1481       if (!strcmp(ext, "DU"))
1482         {
1483           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1484           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1485         }
1486       else if (!strcmp(ext, "FL"))
1487         {
1488           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1489           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1490         }
1491       else
1492         {
1493           for (i = 0; susetags_langtags[i]; i += 2)
1494             {
1495               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1496               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1497             }
1498         }
1499       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1500     }
1501   dataiterator_free(&di);
1502 }
1503
1504 int
1505 susetags_load_ext(Repo *repo, Repodata *data)
1506 {
1507   const char *filename, *descrdir;
1508   Id defvendor;
1509   char ext[3];
1510   FILE *fp;
1511   struct repoinfo *cinfo;
1512   const unsigned char *filechksum;
1513   Id filechksumtype;
1514   int flags;
1515
1516   cinfo = repo->appdata;
1517   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1518   if (!filename)
1519     return 0;
1520   /* susetags load */
1521   ext[0] = filename[9];
1522   ext[1] = filename[10];
1523   ext[2] = 0;
1524   printf("[%s:%s", repo->name, ext);
1525   if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
1526     {
1527       printf(" cached]\n"); fflush(stdout);
1528       return 1;
1529     }
1530   printf(" fetching]\n"); fflush(stdout);
1531   defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1532   descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1533   if (!descrdir)
1534     descrdir = "suse/setup/descr";
1535   filechksumtype = 0;
1536   filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1537   if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1538     return 0;
1539   flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
1540   if (strcmp(ext, "DL") != 0)
1541     flags |= REPO_LOCALPOOL;
1542   if (repo_add_susetags(repo, fp, defvendor, ext, flags))
1543     {
1544       fclose(fp);
1545       printf("%s\n", pool_errstr(repo->pool));
1546       return 0;
1547     }
1548   fclose(fp);
1549   writecachedrepo(repo, data, ext, cinfo->extcookie);
1550   return 1;
1551 }
1552 #endif
1553
1554
1555
1556 /* load callback */
1557
1558 int
1559 load_stub(Pool *pool, Repodata *data, void *dp)
1560 {
1561   struct repoinfo *cinfo = data->repo->appdata;
1562   switch (cinfo->type)
1563     {
1564 #ifdef ENABLE_SUSEREPO
1565     case TYPE_SUSETAGS:
1566       return susetags_load_ext(data->repo, data);
1567 #endif
1568 #ifdef ENABLE_RPMMD
1569     case TYPE_RPMMD:
1570       return repomd_load_ext(data->repo, data);
1571 #endif
1572     default:
1573       return 0;
1574     }
1575 }
1576
1577 static unsigned char installedcookie[32];
1578
1579 #ifdef ENABLE_DEBIAN
1580
1581 const char *
1582 debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
1583 {
1584   char buf[4096];
1585   Id chksumtype;
1586   unsigned char *chksum;
1587   Id curchksumtype;
1588   int l, compl;
1589   char *ch, *fn, *bp;
1590   char *filename;
1591   static char *basearch;
1592   char *binarydir;
1593   int lbinarydir;
1594
1595   if (!basearch)
1596     {
1597       struct utsname un;
1598       if (uname(&un))
1599         {
1600           perror("uname");
1601           exit(1);
1602         }
1603       basearch = strdup(un.machine);
1604       if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
1605         basearch[1] = '3';
1606     }
1607   binarydir = solv_dupjoin("binary-", basearch, "/");
1608   lbinarydir = strlen(binarydir);
1609   compl = strlen(comp);
1610   rewind(fp);
1611   curchksumtype = 0;
1612   filename = 0;
1613   chksum = solv_malloc(32);
1614   chksumtype = 0;
1615   while(fgets(buf, sizeof(buf), fp))
1616     {
1617       l = strlen(buf);
1618       if (l == 0)
1619         continue;
1620       while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
1621         buf[--l] = 0;
1622       if (!strncasecmp(buf, "MD5Sum:", 7))
1623         {
1624           curchksumtype = REPOKEY_TYPE_MD5;
1625           continue;
1626         }
1627       if (!strncasecmp(buf, "SHA1:", 5))
1628         {
1629           curchksumtype = REPOKEY_TYPE_SHA1;
1630           continue;
1631         }
1632       if (!strncasecmp(buf, "SHA256:", 7))
1633         {
1634           curchksumtype = REPOKEY_TYPE_SHA256;
1635           continue;
1636         }
1637       if (!curchksumtype)
1638         continue;
1639       bp = buf;
1640       if (*bp++ != ' ')
1641         {
1642           curchksumtype = 0;
1643           continue;
1644         }
1645       ch = bp;
1646       while (*bp && *bp != ' ' && *bp != '\t')
1647         bp++;
1648       if (!*bp)
1649         continue;
1650       *bp++ = 0;
1651       while (*bp == ' ' || *bp == '\t')
1652         bp++;
1653       while (*bp && *bp != ' ' && *bp != '\t')
1654         bp++;
1655       if (!*bp)
1656         continue;
1657       while (*bp == ' ' || *bp == '\t')
1658         bp++;
1659       fn = bp;
1660       if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
1661         continue;
1662       bp += compl + 1;
1663       if (strncmp(bp, binarydir, lbinarydir))
1664         continue;
1665       bp += lbinarydir;
1666       if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
1667         {
1668           unsigned char curchksum[32];
1669           int curl;
1670           if (filename && !strcmp(bp, "Packages"))
1671             continue;
1672           curl = solv_chksum_len(curchksumtype);
1673           if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
1674             continue;
1675           if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
1676             continue;
1677           solv_free(filename);
1678           filename = strdup(fn);
1679           chksumtype = curchksumtype;
1680           memcpy(chksum, curchksum, curl);
1681         }
1682     }
1683   free(binarydir);
1684   if (filename)
1685     {
1686       fn = solv_dupjoin("/", filename, 0);
1687       solv_free(filename);
1688       filename = solv_dupjoin("dists/", cinfo->name, fn);
1689       solv_free(fn);
1690     }
1691   if (!chksumtype)
1692     chksum = solv_free(chksum);
1693   *chksump = chksum;
1694   *chksumtypep = chksumtype;
1695   return filename;
1696 }
1697 #endif
1698
1699 void
1700 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1701 {
1702   Repo *repo;
1703   struct repoinfo *cinfo;
1704   int i;
1705   FILE *fp;
1706   const char *filename;
1707   const unsigned char *filechksum;
1708   Id filechksumtype;
1709 #ifdef ENABLE_SUSEREPO
1710   const char *descrdir;
1711   int defvendor;
1712 #endif
1713   struct stat stb;
1714   Pool *sigpool = 0;
1715 #if defined(ENABLE_SUSEREPO) || defined(ENABLE_RPMMD)
1716   Repodata *data;
1717 #endif
1718   int dorefresh;
1719 #if defined(ENABLE_DEBIAN)
1720   FILE *fpr;
1721   int j;
1722 #endif
1723
1724   repo = repo_create(pool, "@System");
1725   memset(&stb, 0, sizeof(stb));
1726 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1727   printf("rpm database:");
1728   if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
1729     memset(&stb, 0, sizeof(stb));
1730 #endif
1731 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
1732   printf("dpgk database:");
1733   if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
1734     memset(&stb, 0, sizeof(stb));
1735 #endif
1736   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, 0, installedcookie);
1737   if (usecachedrepo(repo, 0, installedcookie, 0))
1738     printf(" cached\n");
1739   else
1740     {
1741 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1742       FILE *ofp = 0;
1743 #endif
1744       printf(" reading\n");
1745
1746 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1747 # if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
1748       if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1749         {
1750           fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
1751           exit(1);
1752         }
1753 # endif
1754 # if defined(ENABLE_APPDATA)
1755       if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1756         {
1757           fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
1758           exit(1);
1759         }
1760 # endif
1761       ofp = fopen(calccachepath(repo, 0, 0), "r");
1762       if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1763         {
1764           fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
1765           exit(1);
1766         }
1767       if (ofp)
1768         fclose(ofp);
1769 #endif
1770 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
1771       if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1772         {
1773           fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
1774           exit(1);
1775         }
1776 #endif
1777       repo_internalize(repo);
1778       writecachedrepo(repo, 0, 0, installedcookie);
1779     }
1780   pool_set_installed(pool, repo);
1781
1782   for (i = 0; i < nrepoinfos; i++)
1783     {
1784       cinfo = repoinfos + i;
1785       if (!cinfo->enabled)
1786         continue;
1787
1788       repo = repo_create(pool, cinfo->alias);
1789       cinfo->repo = repo;
1790       repo->appdata = cinfo;
1791       repo->priority = 99 - cinfo->priority;
1792
1793       dorefresh = cinfo->autorefresh;
1794       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0, 0), &stb) == 0)
1795         {
1796           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1797             dorefresh = 0;
1798         }
1799       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1800         {
1801           printf("repo '%s':", cinfo->alias);
1802           printf(" cached\n");
1803           continue;
1804         }
1805       switch (cinfo->type)
1806         {
1807 #ifdef ENABLE_RPMMD
1808         case TYPE_RPMMD:
1809           printf("rpmmd repo '%s':", cinfo->alias);
1810           fflush(stdout);
1811           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1812             {
1813               printf(" no repomd.xml file, skipped\n");
1814               repo_free(repo, 1);
1815               cinfo->repo = 0;
1816               break;
1817             }
1818           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1819           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1820             {
1821               printf(" cached\n");
1822               fclose(fp);
1823               break;
1824             }
1825           if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", &sigpool))
1826             {
1827               fclose(fp);
1828               break;
1829             }
1830           if (repo_add_repomdxml(repo, fp, 0))
1831             {
1832               printf("repomd.xml: %s\n", pool_errstr(pool));
1833               fclose(fp);
1834               break;    /* hopeless */
1835             }
1836           fclose(fp);
1837           printf(" fetching\n");
1838           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1839           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1840             {
1841               if (repo_add_rpmmd(repo, fp, 0, 0))
1842                 {
1843                   printf("primary: %s\n", pool_errstr(pool));
1844                   cinfo->incomplete = 1;
1845                 }
1846               fclose(fp);
1847             }
1848           if (cinfo->incomplete)
1849             break;      /* hopeless */
1850
1851           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1852           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1853             {
1854               if (repo_add_updateinfoxml(repo, fp, 0))
1855                 {
1856                   printf("updateinfo: %s\n", pool_errstr(pool));
1857                   cinfo->incomplete = 1;
1858                 }
1859               fclose(fp);
1860             }
1861
1862 #ifdef ENABLE_APPDATA
1863           filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
1864           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1865             {
1866               if (repo_add_appdata(repo, fp, 0))
1867                 {
1868                   printf("appdata: %s\n", pool_errstr(pool));
1869                   cinfo->incomplete = 1;
1870                 }
1871               fclose(fp);
1872             }
1873 #endif
1874           data = repo_add_repodata(repo, 0);
1875           if (!repomd_add_ext(repo, data, "deltainfo"))
1876             repomd_add_ext(repo, data, "prestodelta");
1877           repomd_add_ext(repo, data, "filelists");
1878           repodata_internalize(data);
1879           if (!cinfo->incomplete)
1880             writecachedrepo(repo, 0, 0, cinfo->cookie);
1881           repodata_create_stubs(repo_last_repodata(repo));
1882           break;
1883 #endif
1884
1885 #ifdef ENABLE_SUSEREPO
1886         case TYPE_SUSETAGS:
1887           printf("susetags repo '%s':", cinfo->alias);
1888           fflush(stdout);
1889           descrdir = 0;
1890           defvendor = 0;
1891           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1892             {
1893               printf(" no content file, skipped\n");
1894               repo_free(repo, 1);
1895               cinfo->repo = 0;
1896               break;
1897             }
1898           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1899           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1900             {
1901               printf(" cached\n");
1902               fclose(fp);
1903               break;
1904             }
1905           if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", &sigpool))
1906             {
1907               fclose(fp);
1908               break;
1909             }
1910           if (repo_add_content(repo, fp, 0))
1911             {
1912               printf("content: %s\n", pool_errstr(pool));
1913               fclose(fp);
1914               break;    /* hopeless */
1915             }
1916           fclose(fp);
1917           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1918           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1919           if (!descrdir)
1920             descrdir = "suse/setup/descr";
1921           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1922           if (!filename)
1923             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1924           if (!filename)
1925             {
1926               printf(" no packages file entry, skipped\n");
1927               break;
1928             }
1929           printf(" fetching\n");
1930           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) == 0)
1931             break;      /* hopeless */
1932           if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
1933             {
1934               printf("packages: %s\n", pool_errstr(pool));
1935               fclose(fp);
1936               cinfo->incomplete = 1;
1937               break;    /* hopeless */
1938             }
1939           fclose(fp);
1940           /* add default language */
1941           filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
1942           if (!filename)
1943             filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
1944           if (filename)
1945             {
1946               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1947                 {
1948                   if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
1949                     {
1950                       printf("packages.en: %s\n", pool_errstr(pool));
1951                       cinfo->incomplete = 1;
1952                     }
1953                   fclose(fp);
1954                 }
1955             }
1956           filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
1957           if (filename)
1958             {
1959               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1960                 {
1961                   char pbuf[256];
1962                   while (fgets(pbuf, sizeof(pbuf), fp))
1963                     {
1964                       int l = strlen(pbuf);
1965                       FILE *fp2;
1966                       if (l && pbuf[l - 1] == '\n')
1967                         pbuf[--l] = 0;
1968                       if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
1969                         continue;
1970                       filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
1971                       if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1972                         {
1973                           if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
1974                             {
1975                               printf("%s: %s\n", pbuf, pool_errstr(pool));
1976                               cinfo->incomplete = 1;
1977                             }
1978                           fclose(fp2);
1979                         }
1980                     }
1981                   fclose(fp);
1982                 }
1983             }
1984 #ifdef ENABLE_APPDATA
1985           filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
1986           if (!filename)
1987             filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
1988           if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1989             {
1990               if (repo_add_appdata(repo, fp, 0))
1991                 {
1992                   printf("appdata: %s\n", pool_errstr(pool));
1993                   cinfo->incomplete = 1;
1994                 }
1995               fclose(fp);
1996             }
1997 #endif
1998           repo_internalize(repo);
1999           data = repo_add_repodata(repo, 0);
2000           susetags_add_ext(repo, data);
2001           repodata_internalize(data);
2002           if (!cinfo->incomplete)
2003             writecachedrepo(repo, 0, 0, cinfo->cookie);
2004           repodata_create_stubs(repo_last_repodata(repo));
2005           break;
2006 #endif
2007
2008 #if defined(ENABLE_DEBIAN)
2009         case TYPE_DEBIAN:
2010           printf("debian repo '%s':", cinfo->alias);
2011           fflush(stdout);
2012           filename = solv_dupjoin("dists/", cinfo->name, "/Release");
2013           if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
2014             {
2015               printf(" no Release file, skipped\n");
2016               repo_free(repo, 1);
2017               cinfo->repo = 0;
2018               free((char *)filename);
2019               break;
2020             }
2021           solv_free((char *)filename);
2022           if (cinfo->repo_gpgcheck)
2023             {
2024               filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
2025               if (!downloadchecksig(cinfo, fpr, filename, &sigpool))
2026                 {
2027                   fclose(fpr);
2028                   solv_free((char *)filename);
2029                   break;
2030                 }
2031               solv_free((char *)filename);
2032             }
2033           calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
2034           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
2035             {
2036               printf(" cached\n");
2037               fclose(fpr);
2038               break;
2039             }
2040           printf(" fetching\n");
2041           for (j = 0; j < cinfo->ncomponents; j++)
2042             {
2043               if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
2044                 {
2045                   printf("[component %s not found]\n", cinfo->components[j]);
2046                   continue;
2047                 }
2048               if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
2049                 {
2050                   if (repo_add_debpackages(repo, fp, 0))
2051                     {
2052                       printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
2053                       cinfo->incomplete = 1;
2054                     }
2055                   fclose(fp);
2056                 }
2057               solv_free((char *)filechksum);
2058               solv_free((char *)filename);
2059             }
2060           fclose(fpr);
2061           if (!cinfo->incomplete)
2062             writecachedrepo(repo, 0, 0, cinfo->cookie);
2063           break;
2064 #endif
2065
2066         default:
2067           printf("unsupported repo '%s': skipped\n", cinfo->alias);
2068           repo_free(repo, 1);
2069           cinfo->repo = 0;
2070           break;
2071         }
2072     }
2073   if (sigpool)
2074     pool_free(sigpool);
2075 }
2076
2077 int
2078 yesno(const char *str)
2079 {
2080   char inbuf[128], *ip;
2081
2082   for (;;)
2083     {
2084       printf("%s", str);
2085       fflush(stdout);
2086       *inbuf = 0;
2087       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2088         {
2089           printf("Abort.\n");
2090           exit(1);
2091         }
2092       while (*ip == ' ' || *ip == '\t')
2093         ip++;
2094       if (*ip == 'q')
2095         {
2096           printf("Abort.\n");
2097           exit(1);
2098         }
2099       if (*ip == 'y' || *ip == 'n')
2100         return *ip == 'y' ? 1 : 0;
2101     }
2102 }
2103
2104 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2105
2106 struct fcstate {
2107   FILE **newpkgsfps;
2108   Queue *checkq;
2109   int newpkgscnt;
2110   void *rpmstate;
2111 };
2112
2113 static void *
2114 fileconflict_cb(Pool *pool, Id p, void *cbdata)
2115 {
2116   struct fcstate *fcstate = cbdata;
2117   Solvable *s;
2118   Id rpmdbid;
2119   int i;
2120   FILE *fp;
2121
2122   s = pool_id2solvable(pool, p);
2123   if (pool->installed && s->repo == pool->installed)
2124     {
2125       if (!s->repo->rpmdbid)
2126         return 0;
2127       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
2128       if (!rpmdbid)
2129         return 0;
2130       return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
2131     }
2132   for (i = 0; i < fcstate->newpkgscnt; i++)
2133     if (fcstate->checkq->elements[i] == p)
2134       break;
2135   if (i == fcstate->newpkgscnt)
2136     return 0;
2137   fp = fcstate->newpkgsfps[i];
2138   if (!fp)
2139     return 0;
2140   rewind(fp);
2141   return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
2142 }
2143
2144
2145 void
2146 runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
2147 {
2148   pid_t pid;
2149   int status;
2150
2151   if ((pid = fork()) == (pid_t)-1)
2152     {
2153       perror("fork");
2154       exit(1);
2155     }
2156   if (pid == 0)
2157     {
2158       if (!rootdir)
2159         rootdir = "/";
2160       if (dupfd3 != -1 && dupfd3 != 3)
2161         {
2162           dup2(dupfd3, 3);
2163           close(dupfd3);
2164         }
2165       if (dupfd3 != -1)
2166         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2167       if (strcmp(arg, "-e") == 0)
2168         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
2169       else
2170         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
2171       perror("rpm");
2172       _exit(0);
2173     }
2174   while (waitpid(pid, &status, 0) != pid)
2175     ;
2176   if (status)
2177     {
2178       printf("rpm failed\n");
2179       exit(1);
2180     }
2181 }
2182
2183 #endif
2184
2185 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2186
2187 void
2188 rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
2189 {
2190   pid_t pid;
2191   int status;
2192
2193   if ((pid = fork()) == (pid_t)-1)
2194     {
2195       perror("fork");
2196       exit(1);
2197     }
2198   if (pid == 0)
2199     {
2200       if (!rootdir)
2201         rootdir = "/";
2202       if (dupfd3 != -1 && dupfd3 != 3)
2203         {
2204           dup2(dupfd3, 3);
2205           close(dupfd3);
2206         }
2207       if (dupfd3 != -1)
2208         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2209       if (strcmp(arg, "--install") == 0)
2210         execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
2211       else
2212         execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
2213       perror("dpkg");
2214       _exit(0);
2215     }
2216   while (waitpid(pid, &status, 0) != pid)
2217     ;
2218   if (status)
2219     {
2220       printf("dpkg failed\n");
2221       exit(1);
2222     }
2223 }
2224
2225 #endif
2226
2227 #ifdef SUSE
2228 static Id
2229 nscallback(Pool *pool, void *data, Id name, Id evr)
2230 {
2231 #if 0
2232   if (name == NAMESPACE_LANGUAGE)
2233     {
2234       if (!strcmp(pool_id2str(pool, evr), "ja"))
2235         return 1;
2236       if (!strcmp(pool_id2str(pool, evr), "de"))
2237         return 1;
2238       if (!strcmp(pool_id2str(pool, evr), "en"))
2239         return 1;
2240       if (!strcmp(pool_id2str(pool, evr), "en_US"))
2241         return 1;
2242     }
2243 #endif
2244   return 0;
2245 }
2246 #endif
2247
2248 #ifdef SOFTLOCKS_PATH
2249 void
2250 addsoftlocks(Pool *pool, Queue *job)
2251 {
2252   FILE *fp;
2253   Id type, id, p, pp;
2254   char *bp, *ep, buf[4096];
2255
2256   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2257     return;
2258   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2259     {
2260       while (*bp == ' ' || *bp == '\t')
2261         bp++;
2262       if (!*bp || *bp == '#')
2263         continue;
2264       for (ep = bp; *ep; ep++)
2265         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2266           break;
2267       *ep = 0;
2268       type = SOLVER_SOLVABLE_NAME;
2269       if (!strncmp(bp, "provides:", 9) && bp[9])
2270         {
2271           type = SOLVER_SOLVABLE_PROVIDES;
2272           bp += 9;
2273         }
2274       id = pool_str2id(pool, bp, 1);
2275       if (pool->installed)
2276         {
2277           FOR_JOB_SELECT(p, pp, type, id)
2278             if (pool->solvables[p].repo == pool->installed)
2279               break;
2280           if (p)
2281             continue;   /* ignore, as it is already installed */
2282         }
2283       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2284     }
2285   fclose(fp);
2286 }
2287 #endif
2288
2289
2290 #if defined(ENABLE_RPMDB)
2291
2292 static void
2293 rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
2294 {
2295   Repo *repo;
2296   Repodata *data;
2297   Map providedids;
2298   Queue fileprovidesq;
2299   int i, j, n;
2300   struct repoinfo *cinfo;
2301
2302   map_init(&providedids, pool->ss.nstrings);
2303   queue_init(&fileprovidesq);
2304   for (i = 0; i < addedfileprovides->count; i++)
2305     MAPSET(&providedids, addedfileprovides->elements[i]);
2306   FOR_REPOS(i, repo)
2307     {
2308       /* make sure all repodatas but the first are extensions */
2309       if (repo->nrepodata < 2)
2310         continue;
2311       cinfo = repo->appdata;
2312       if (repo != pool->installed && !cinfo)
2313         continue;
2314       if (cinfo && cinfo->incomplete)
2315         continue;
2316       data = repo_id2repodata(repo, 1);
2317       if (data->loadcallback)
2318         continue;
2319       for (j = 2; j < repo->nrepodata; j++)
2320         {
2321           Repodata *edata = repo_id2repodata(repo, j);
2322           if (!edata->loadcallback)
2323             break;
2324         }
2325       if (j < repo->nrepodata)
2326         continue;       /* found a non-externsion repodata, can't rewrite  */
2327       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2328         {
2329           if (repo == pool->installed && addedfileprovides_inst)
2330             {
2331               for (j = 0; j < addedfileprovides->count; j++)
2332                 MAPCLR(&providedids, addedfileprovides->elements[j]);
2333               for (j = 0; j < addedfileprovides_inst->count; j++)
2334                 MAPSET(&providedids, addedfileprovides_inst->elements[j]);
2335             }
2336           n = 0;
2337           for (j = 0; j < fileprovidesq.count; j++)
2338             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2339               n++;
2340           if (repo == pool->installed && addedfileprovides_inst)
2341             {
2342               for (j = 0; j < addedfileprovides_inst->count; j++)
2343                 MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
2344               for (j = 0; j < addedfileprovides->count; j++)
2345                 MAPSET(&providedids, addedfileprovides->elements[j]);
2346               if (n == addedfileprovides_inst->count)
2347                 continue;       /* nothing new added */
2348             }
2349           else if (n == addedfileprovides->count)
2350             continue;   /* nothing new added */
2351         }
2352       repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
2353       repodata_internalize(data);
2354       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2355     }
2356   queue_free(&fileprovidesq);
2357   map_free(&providedids);
2358 }
2359
2360 static void
2361 addfileprovides(Pool *pool)
2362 {
2363   Queue addedfileprovides;
2364   Queue addedfileprovides_inst;
2365
2366   queue_init(&addedfileprovides);
2367   queue_init(&addedfileprovides_inst);
2368   pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
2369   if (addedfileprovides.count || addedfileprovides_inst.count)
2370     rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
2371   queue_free(&addedfileprovides);
2372   queue_free(&addedfileprovides_inst);
2373 }
2374
2375 #endif
2376
2377 #ifdef SUSE
2378 static void
2379 add_autopackages(Pool *pool)
2380 {
2381   int i;
2382   Repo *repo;
2383   FOR_REPOS(i, repo)
2384     repo_add_autopattern(repo, 0);
2385 }
2386 #endif
2387
2388 #if defined(SUSE) || defined(FEDORA)
2389 static void
2390 add_patchjobs(Pool *pool, Queue *job)
2391 {
2392   Id p, pp;
2393   int pruneyou = 0;
2394   Map installedmap, multiversionmap;
2395   Solvable *s;
2396
2397   map_init(&multiversionmap, 0);
2398   map_init(&installedmap, pool->nsolvables);
2399   solver_calculate_multiversionmap(pool, job, &multiversionmap);
2400   if (pool->installed)
2401     FOR_REPO_SOLVABLES(pool->installed, p, s)
2402       MAPSET(&installedmap, p);
2403
2404   /* install all patches */
2405   for (p = 1; p < pool->nsolvables; p++)
2406     {
2407       const char *type;
2408       int r;
2409       Id p2;
2410
2411       s = pool->solvables + p;
2412       if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
2413         continue;
2414       FOR_PROVIDES(p2, pp, s->name)
2415         {
2416           Solvable *s2 = pool->solvables + p2;
2417           if (s2->name != s->name)
2418             continue;
2419           r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2420           if (r < 0 || (r == 0 && p > p2))
2421             break;
2422         }
2423       if (p2)
2424         continue;
2425       type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2426       if (type && !strcmp(type, "optional"))
2427         continue;
2428       r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
2429       if (r == -1)
2430         continue;
2431       if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2432         {
2433           if (!pruneyou++)
2434             queue_empty(job);
2435         }
2436       else if (pruneyou)
2437         continue;
2438       queue_push2(job, SOLVER_SOLVABLE, p);
2439     }
2440   map_free(&installedmap);
2441   map_free(&multiversionmap);
2442 }
2443 #endif
2444
2445 #ifdef SUSE
2446 static void
2447 showdiskusagechanges(Transaction *trans)
2448 {
2449   DUChanges duc[4];
2450   int i;
2451
2452   /* XXX: use mountpoints here */
2453   memset(duc, 0, sizeof(duc));
2454   duc[0].path = "/";
2455   duc[1].path = "/usr/share/man";
2456   duc[2].path = "/sbin";
2457   duc[3].path = "/etc";
2458   transaction_calc_duchanges(trans, duc, 4);
2459   for (i = 0; i < 4; i++)
2460     printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2461 }
2462 #endif
2463
2464 #if defined(ENABLE_RPMDB)
2465 static FILE *
2466 trydeltadownload(Solvable *s, struct repoinfo *cinfo, const char *loc)
2467 {
2468   Pool *pool = s->repo->pool;
2469   Dataiterator di;
2470   Id pp;
2471   const unsigned char *chksum;
2472   Id chksumtype;
2473   FILE *retfp = 0;
2474   char *matchname = strdup(pool_id2str(pool, s->name));
2475
2476   dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2477   dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2478   while (dataiterator_step(&di))
2479     {
2480       Id baseevr, op;
2481
2482       dataiterator_setpos_parent(&di);
2483       if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2484           pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2485         continue;
2486       baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2487       FOR_PROVIDES(op, pp, s->name)
2488         {
2489           Solvable *os = pool->solvables + op;
2490           if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2491             break;
2492         }
2493       if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2494         {
2495           /* base is installed, run sequence check */
2496           const char *seq;
2497           const char *dloc;
2498           const char *archstr;
2499           FILE *fp;
2500           char cmd[128];
2501           int newfd;
2502
2503           archstr = pool_id2str(pool, s->arch);
2504           if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
2505             continue;
2506
2507           seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
2508           seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
2509           if (strchr(seq, '\'') != 0)
2510             continue;
2511 #ifdef FEDORA
2512           sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
2513 #else
2514           sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
2515 #endif
2516           if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
2517             continue;   /* didn't match */
2518           /* looks good, download delta */
2519           chksumtype = 0;
2520           chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2521           if (!chksumtype)
2522             continue;   /* no way! */
2523           dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
2524           if (!dloc)
2525             continue;
2526 #ifdef ENABLE_SUSEREPO
2527           if (cinfo->type == TYPE_SUSETAGS)
2528             {
2529               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2530               dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
2531             }
2532 #endif
2533           if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2534             continue;
2535           /* got it, now reconstruct */
2536           newfd = opentmpfile();
2537 #ifdef FEDORA
2538           sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
2539 #else
2540           sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2541 #endif
2542           fcntl(fileno(fp), F_SETFD, 0);
2543           if (system(cmd))
2544             {
2545               close(newfd);
2546               fclose(fp);
2547               continue;
2548             }
2549           lseek(newfd, 0, SEEK_SET);
2550           chksumtype = 0;
2551           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2552           if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2553             {
2554               close(newfd);
2555               fclose(fp);
2556               continue;
2557             }
2558           retfp = fdopen(newfd, "r");
2559           fclose(fp);
2560           break;
2561         }
2562     }
2563   dataiterator_free(&di);
2564   solv_free(matchname);
2565   return retfp;
2566 }
2567 #endif
2568
2569
2570 #define MODE_LIST        0
2571 #define MODE_INSTALL     1
2572 #define MODE_ERASE       2
2573 #define MODE_UPDATE      3
2574 #define MODE_DISTUPGRADE 4
2575 #define MODE_VERIFY      5
2576 #define MODE_PATCH       6
2577 #define MODE_INFO        7
2578 #define MODE_REPOLIST    8
2579 #define MODE_SEARCH      9
2580
2581 void
2582 usage(int r)
2583 {
2584   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2585   fprintf(stderr, "\n");
2586   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2587   fprintf(stderr, "                  versions from the repositories\n");
2588   fprintf(stderr, "    erase:        erase installed packages\n");
2589   fprintf(stderr, "    info:         display package information\n");
2590   fprintf(stderr, "    install:      install packages\n");
2591   fprintf(stderr, "    list:         list packages\n");
2592   fprintf(stderr, "    repos:        list enabled repositories\n");
2593   fprintf(stderr, "    search:       search name/summary/description\n");
2594   fprintf(stderr, "    update:       update installed packages\n");
2595   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2596 #if defined(SUSE) || defined(FEDORA)
2597   fprintf(stderr, "    patch:        install newest patches\n");
2598 #endif
2599   fprintf(stderr, "\n");
2600   exit(r);
2601 }
2602
2603 int
2604 main(int argc, char **argv)
2605 {
2606   Pool *pool;
2607   Repo *commandlinerepo = 0;
2608   Id *commandlinepkgs = 0;
2609   Id p;
2610   struct repoinfo *repoinfos;
2611   int nrepoinfos = 0;
2612   int mainmode = 0, mode = 0;
2613   int i, newpkgs;
2614   Queue job, checkq;
2615   Solver *solv = 0;
2616   Transaction *trans;
2617   FILE **newpkgsfps;
2618   Queue repofilter;
2619   Queue kindfilter;
2620   Queue archfilter;
2621   int archfilter_src = 0;
2622   int cleandeps = 0;
2623   int forcebest = 0;
2624   char *rootdir = 0;
2625   char *keyname = 0;
2626   int debuglevel = 0;
2627
2628   argc--;
2629   argv++;
2630   userhome = getenv("HOME");
2631   if (userhome && userhome[0] != '/')
2632     userhome = 0;
2633   while (argc && !strcmp(argv[0], "-d"))
2634     {
2635       debuglevel++;
2636       argc--;
2637       argv++;
2638     }
2639   if (!argv[0])
2640     usage(1);
2641   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2642     {
2643       mainmode = MODE_INSTALL;
2644       mode = SOLVER_INSTALL;
2645     }
2646 #if defined(SUSE) || defined(FEDORA)
2647   else if (!strcmp(argv[0], "patch"))
2648     {
2649       mainmode = MODE_PATCH;
2650       mode = SOLVER_INSTALL;
2651     }
2652 #endif
2653   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2654     {
2655       mainmode = MODE_ERASE;
2656       mode = SOLVER_ERASE;
2657     }
2658   else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
2659     {
2660       mainmode = MODE_LIST;
2661       mode = 0;
2662     }
2663   else if (!strcmp(argv[0], "info"))
2664     {
2665       mainmode = MODE_INFO;
2666       mode = 0;
2667     }
2668   else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
2669     {
2670       mainmode = MODE_SEARCH;
2671       mode = 0;
2672     }
2673   else if (!strcmp(argv[0], "verify"))
2674     {
2675       mainmode = MODE_VERIFY;
2676       mode = SOLVER_VERIFY;
2677     }
2678   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2679     {
2680       mainmode = MODE_UPDATE;
2681       mode = SOLVER_UPDATE;
2682     }
2683   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2684     {
2685       mainmode = MODE_DISTUPGRADE;
2686       mode = SOLVER_DISTUPGRADE;
2687     }
2688   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2689     {
2690       mainmode = MODE_REPOLIST;
2691       mode = 0;
2692     }
2693   else
2694     usage(1);
2695
2696   for (;;)
2697     {
2698       if (argc > 2 && !strcmp(argv[1], "--root"))
2699         {
2700           rootdir = argv[2];
2701           argc -= 2;
2702           argv += 2;
2703         }
2704       else if (argc > 1 && !strcmp(argv[1], "--clean"))
2705         {
2706           cleandeps = 1;
2707           argc--;
2708           argv++;
2709         }
2710       else if (argc > 1 && !strcmp(argv[1], "--best"))
2711         {
2712           forcebest = 1;
2713           argc--;
2714           argv++;
2715         }
2716       if (argc > 2 && !strcmp(argv[1], "--keyname"))
2717         {
2718           keyname = argv[2];
2719           argc -= 2;
2720           argv += 2;
2721         }
2722       else
2723         break;
2724     }
2725
2726   pool = pool_create();
2727   pool_set_rootdir(pool, rootdir);
2728
2729 #if 0
2730   {
2731     const char *langs[] = {"de_DE", "de", "en"};
2732     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
2733   }
2734 #endif
2735
2736   pool_setloadcallback(pool, load_stub, 0);
2737 #ifdef SUSE
2738   pool->nscallback = nscallback;
2739 #endif
2740   if (debuglevel)
2741     pool_setdebuglevel(pool, debuglevel);
2742   setarch(pool);
2743   pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
2744   repoinfos = read_repoinfos(pool, &nrepoinfos);
2745
2746   if (mainmode == MODE_REPOLIST)
2747     {
2748       int j = 1;
2749       for (i = 0; i < nrepoinfos; i++)
2750         {
2751           struct repoinfo *cinfo = repoinfos + i;
2752           if (!cinfo->enabled)
2753             continue;
2754           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
2755         }
2756       exit(0);
2757     }
2758
2759   read_repos(pool, repoinfos, nrepoinfos);
2760
2761   /* setup filters */
2762   queue_init(&repofilter);
2763   queue_init(&kindfilter);
2764   queue_init(&archfilter);
2765   while (argc > 1)
2766     {
2767       if (!strcmp(argv[1], "-i"))
2768         {
2769           queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
2770           argc--;
2771           argv++;
2772         }
2773       else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
2774         {
2775           const char *rname = argv[2], *rp;
2776           Id repoid = 0;
2777           for (rp = rname; *rp; rp++)
2778             if (*rp <= '0' || *rp >= '9')
2779               break;
2780           if (!*rp)
2781             {
2782               /* repo specified by number */
2783               int rnum = atoi(rname);
2784               for (i = 0; i < nrepoinfos; i++)
2785                 {
2786                   struct repoinfo *cinfo = repoinfos + i;
2787                   if (!cinfo->enabled)
2788                     continue;
2789                   if (--rnum == 0)
2790                     repoid = cinfo->repo->repoid;
2791                 }
2792             }
2793           else
2794             {
2795               /* repo specified by alias */
2796               Repo *repo;
2797               FOR_REPOS(i, repo)
2798                 {
2799                   if (!strcasecmp(rname, repo->name))
2800                     repoid = repo->repoid;
2801                 }
2802             }
2803           if (!repoid)
2804             {
2805               fprintf(stderr, "%s: no such repo\n", rname);
2806               exit(1);
2807             }
2808           /* SETVENDOR is actually wrong but useful */
2809           queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
2810           argc -= 2;
2811           argv += 2;
2812         }
2813       else if (argc > 2 && !strcmp(argv[1], "--arch"))
2814         {
2815           if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
2816             archfilter_src = 1;
2817           queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
2818           argc -= 2;
2819           argv += 2;
2820         }
2821       else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
2822         {
2823           const char *kind = argv[2];
2824           if (!strcmp(kind, "srcpackage"))
2825             {
2826               /* hey! should use --arch! */
2827               queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
2828               archfilter_src = 1;
2829               argc -= 2;
2830               argv += 2;
2831               continue;
2832             }
2833           if (!strcmp(kind, "package"))
2834             kind = "";
2835           if (!strcmp(kind, "all"))
2836             queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
2837           else
2838             queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
2839           argc -= 2;
2840           argv += 2;
2841         }
2842       else
2843         break;
2844     }
2845
2846   if (mainmode == MODE_SEARCH)
2847     {
2848       Queue sel, q;
2849       Dataiterator di;
2850       if (argc != 2)
2851         usage(1);
2852       pool_createwhatprovides(pool);
2853       queue_init(&sel);
2854       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2855       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2856       dataiterator_set_search(&di, 0, 0);
2857       while (dataiterator_step(&di))
2858         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
2859       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2860       dataiterator_set_search(&di, 0, 0);
2861       while (dataiterator_step(&di))
2862         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
2863       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2864       dataiterator_set_search(&di, 0, 0);
2865       while (dataiterator_step(&di))
2866         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
2867       dataiterator_free(&di);
2868       if (repofilter.count)
2869         selection_filter(pool, &sel, &repofilter);
2870         
2871       queue_init(&q);
2872       selection_solvables(pool, &sel, &q);
2873       queue_free(&sel);
2874       for (i = 0; i < q.count; i++)
2875         {
2876           Solvable *s = pool_id2solvable(pool, q.elements[i]);
2877           printf("  - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
2878         }
2879       queue_free(&q);
2880       exit(0);
2881     }
2882
2883   /* process command line packages */
2884   if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
2885     {
2886       for (i = 1; i < argc; i++)
2887         {
2888           int l;
2889           l = strlen(argv[i]);
2890 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2891           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2892             continue;
2893 #endif
2894 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2895           if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
2896             continue;
2897 #endif
2898           if (access(argv[i], R_OK))
2899             {
2900               perror(argv[i]);
2901               exit(1);
2902             }
2903           if (!commandlinepkgs)
2904             commandlinepkgs = solv_calloc(argc, sizeof(Id));
2905           if (!commandlinerepo)
2906             commandlinerepo = repo_create(pool, "@commandline");
2907           p = 0;
2908 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2909           p = repo_add_rpm(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2910 #endif
2911 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2912           p = repo_add_deb(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2913 #endif
2914           if (!p)
2915             {
2916               fprintf(stderr, "could not add '%s'\n", argv[i]);
2917               exit(1);
2918             }
2919           commandlinepkgs[i] = p;
2920         }
2921       if (commandlinerepo)
2922         repo_internalize(commandlinerepo);
2923     }
2924
2925   // FOR_REPOS(i, repo)
2926   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2927
2928 #if defined(ENABLE_RPMDB)
2929   if (pool->disttype == DISTTYPE_RPM)
2930     addfileprovides(pool);
2931 #endif
2932 #ifdef SUSE
2933   add_autopackages(pool);
2934 #endif
2935   pool_createwhatprovides(pool);
2936
2937   if (keyname)
2938     keyname = solv_dupjoin("solvable:", keyname, 0);
2939   queue_init(&job);
2940   for (i = 1; i < argc; i++)
2941     {
2942       Queue job2;
2943       int j, flags, rflags;
2944
2945       if (commandlinepkgs && commandlinepkgs[i])
2946         {
2947           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2948           continue;
2949         }
2950       queue_init(&job2);
2951       flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
2952       flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
2953       if (kindfilter.count)
2954         flags |= SELECTION_SKIP_KIND;
2955       if (mode == MODE_LIST || archfilter_src)
2956         flags |= SELECTION_WITH_SOURCE;
2957       if (argv[i][0] == '/')
2958         flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
2959       if (!keyname)
2960         rflags = selection_make(pool, &job2, argv[i], flags);
2961       else
2962         rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
2963       if (repofilter.count)
2964         selection_filter(pool, &job2, &repofilter);
2965       if (archfilter.count)
2966         selection_filter(pool, &job2, &archfilter);
2967       if (kindfilter.count)
2968         selection_filter(pool, &job2, &kindfilter);
2969       if (!job2.count)
2970         {
2971           flags |= SELECTION_NOCASE;
2972           if (!keyname)
2973             rflags = selection_make(pool, &job2, argv[i], flags);
2974           else
2975             rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
2976           if (repofilter.count)
2977             selection_filter(pool, &job2, &repofilter);
2978           if (archfilter.count)
2979             selection_filter(pool, &job2, &archfilter);
2980           if (kindfilter.count)
2981             selection_filter(pool, &job2, &kindfilter);
2982           if (job2.count)
2983             printf("[ignoring case for '%s']\n", argv[i]);
2984         }
2985       if (!job2.count)
2986         {
2987           fprintf(stderr, "nothing matches '%s'\n", argv[i]);
2988           exit(1);
2989         }
2990       if (rflags & SELECTION_FILELIST)
2991         printf("[using file list match for '%s']\n", argv[i]);
2992       if (rflags & SELECTION_PROVIDES)
2993         printf("[using capability match for '%s']\n", argv[i]);
2994       for (j = 0; j < job2.count; j++)
2995         queue_push(&job, job2.elements[j]);
2996       queue_free(&job2);
2997     }
2998   keyname = solv_free(keyname);
2999
3000   if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
3001     {
3002       queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
3003       if (repofilter.count)
3004         selection_filter(pool, &job, &repofilter);
3005       if (archfilter.count)
3006         selection_filter(pool, &job, &archfilter);
3007       if (kindfilter.count)
3008         selection_filter(pool, &job, &kindfilter);
3009     }
3010   queue_free(&repofilter);
3011   queue_free(&archfilter);
3012   queue_free(&kindfilter);
3013
3014   if (!job.count && mainmode != MODE_PATCH)
3015     {
3016       printf("no package matched\n");
3017       exit(1);
3018     }
3019
3020   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
3021     {
3022       /* list mode, no solver needed */
3023       Queue q;
3024       queue_init(&q);
3025       for (i = 0; i < job.count; i += 2)
3026         {
3027           int j;
3028           queue_empty(&q);
3029           pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
3030           for (j = 0; j < q.count; j++)
3031             {
3032               Solvable *s = pool_id2solvable(pool, q.elements[j]);
3033               if (mainmode == MODE_INFO)
3034                 {
3035                   const char *str;
3036                   printf("Name:        %s\n", pool_solvable2str(pool, s));
3037                   printf("Repo:        %s\n", s->repo->name);
3038                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
3039                   str = solvable_lookup_str(s, SOLVABLE_URL);
3040                   if (str)
3041                     printf("Url:         %s\n", str);
3042                   str = solvable_lookup_str(s, SOLVABLE_LICENSE);
3043                   if (str)
3044                     printf("License:     %s\n", str);
3045 #if 0
3046                   str = solvable_lookup_sourcepkg(s);
3047                   if (str)
3048                     printf("Source:      %s\n", str);
3049 #endif
3050                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
3051                   printf("\n");
3052                 }
3053               else
3054                 {
3055 #if 1
3056                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
3057 #else
3058                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
3059 #endif
3060                   printf("  - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
3061                   if (sum)
3062                     printf("    %s\n", sum);
3063                 }
3064             }
3065         }
3066       queue_free(&q);
3067       queue_free(&job);
3068       pool_free(pool);
3069       free_repoinfos(repoinfos, nrepoinfos);
3070       solv_free(commandlinepkgs);
3071 #ifdef FEDORA
3072       yum_substitute(pool, 0);
3073 #endif
3074       exit(0);
3075     }
3076
3077 #if defined(SUSE) || defined(FEDORA)
3078   if (mainmode == MODE_PATCH)
3079     add_patchjobs(pool, &job);
3080 #endif
3081
3082   // add mode
3083   for (i = 0; i < job.count; i += 2)
3084     {
3085       job.elements[i] |= mode;
3086       if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
3087         job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
3088       if (cleandeps)
3089         job.elements[i] |= SOLVER_CLEANDEPS;
3090       if (forcebest)
3091         job.elements[i] |= SOLVER_FORCEBEST;
3092     }
3093
3094   // multiversion test
3095   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
3096   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
3097   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
3098 #if 0
3099   queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
3100   queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
3101 #endif
3102
3103 #ifdef SOFTLOCKS_PATH
3104   addsoftlocks(pool, &job);
3105 #endif
3106
3107 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3108 rerunsolver:
3109 #endif
3110   solv = solver_create(pool);
3111   solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
3112 #ifdef FEDORA
3113   solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
3114 #endif
3115   if (mainmode == MODE_ERASE)
3116     solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);      /* don't nag */
3117   solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
3118
3119   for (;;)
3120     {
3121       Id problem, solution;
3122       int pcnt, scnt;
3123
3124       if (!solver_solve(solv, &job))
3125         break;
3126       pcnt = solver_problem_count(solv);
3127       printf("Found %d problems:\n", pcnt);
3128       for (problem = 1; problem <= pcnt; problem++)
3129         {
3130           int take = 0;
3131           printf("Problem %d/%d:\n", problem, pcnt);
3132           solver_printprobleminfo(solv, problem);
3133           printf("\n");
3134           scnt = solver_solution_count(solv, problem);
3135           for (solution = 1; solution <= scnt; solution++)
3136             {
3137               printf("Solution %d:\n", solution);
3138               solver_printsolution(solv, problem, solution);
3139               printf("\n");
3140             }
3141           for (;;)
3142             {
3143               char inbuf[128], *ip;
3144               printf("Please choose a solution: ");
3145               fflush(stdout);
3146               *inbuf = 0;
3147               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
3148                 {
3149                   printf("Abort.\n");
3150                   exit(1);
3151                 }
3152               while (*ip == ' ' || *ip == '\t')
3153                 ip++;
3154               if (*ip >= '0' && *ip <= '9')
3155                 {
3156                   take = atoi(ip);
3157                   if (take >= 1 && take <= scnt)
3158                     break;
3159                 }
3160               if (*ip == 's')
3161                 {
3162                   take = 0;
3163                   break;
3164                 }
3165               if (*ip == 'q')
3166                 {
3167                   printf("Abort.\n");
3168                   exit(1);
3169                 }
3170             }
3171           if (!take)
3172             continue;
3173           solver_take_solution(solv, problem, take, &job);
3174         }
3175     }
3176
3177   trans = solver_create_transaction(solv);
3178   if (!trans->steps.count)
3179     {
3180       printf("Nothing to do.\n");
3181       transaction_free(trans);
3182       solver_free(solv);
3183       queue_free(&job);
3184       pool_free(pool);
3185       free_repoinfos(repoinfos, nrepoinfos);
3186       solv_free(commandlinepkgs);
3187 #ifdef FEDORA
3188       yum_substitute(pool, 0);
3189 #endif
3190       exit(1);
3191     }
3192
3193   /* display transaction to the user and ask for confirmation */
3194   printf("\n");
3195   printf("Transaction summary:\n\n");
3196   transaction_print(trans);
3197 #if defined(SUSE)
3198   showdiskusagechanges(trans);
3199 #endif
3200   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
3201   printf("\n");
3202
3203   if (!yesno("OK to continue (y/n)? "))
3204     {
3205       printf("Abort.\n");
3206       transaction_free(trans);
3207       solver_free(solv);
3208       queue_free(&job);
3209       pool_free(pool);
3210       free_repoinfos(repoinfos, nrepoinfos);
3211       solv_free(commandlinepkgs);
3212 #ifdef FEDORA
3213       yum_substitute(pool, 0);
3214 #endif
3215       exit(1);
3216     }
3217
3218   /* download all new packages */
3219   queue_init(&checkq);
3220   newpkgs = transaction_installedresult(trans, &checkq);
3221   newpkgsfps = 0;
3222   if (newpkgs)
3223     {
3224       int downloadsize = 0;
3225       for (i = 0; i < newpkgs; i++)
3226         {
3227           Solvable *s;
3228
3229           p = checkq.elements[i];
3230           s = pool_id2solvable(pool, p);
3231           downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
3232         }
3233       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
3234       newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
3235       for (i = 0; i < newpkgs; i++)
3236         {
3237           unsigned int medianr;
3238           const char *loc;
3239           Solvable *s;
3240           struct repoinfo *cinfo;
3241           const unsigned char *chksum;
3242           Id chksumtype;
3243
3244           p = checkq.elements[i];
3245           s = pool_id2solvable(pool, p);
3246           if (s->repo == commandlinerepo)
3247             {
3248               loc = solvable_lookup_location(s, &medianr);
3249               if (!loc)
3250                 continue;
3251               if (!(newpkgsfps[i] = fopen(loc, "r")))
3252                 {
3253                   perror(loc);
3254                   exit(1);
3255                 }
3256               putchar('.');
3257               continue;
3258             }
3259           cinfo = s->repo->appdata;
3260           if (!cinfo)
3261             {
3262               printf("%s: no repository information\n", s->repo->name);
3263               exit(1);
3264             }
3265           loc = solvable_lookup_location(s, &medianr);
3266           if (!loc)
3267              continue;
3268 #if defined(ENABLE_RPMDB)
3269           if (pool->installed && pool->installed->nsolvables)
3270             {
3271               if ((newpkgsfps[i] = trydeltadownload(s, cinfo, loc)) != 0)
3272                 {
3273                   putchar('d');
3274                   fflush(stdout);
3275                   continue;             /* delta worked! */
3276                 }
3277             }
3278 #endif
3279 #ifdef ENABLE_SUSEREPO
3280           if (cinfo->type == TYPE_SUSETAGS)
3281             {
3282               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
3283               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
3284             }
3285 #endif
3286           chksumtype = 0;
3287           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3288           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
3289             {
3290               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
3291               exit(1);
3292             }
3293           putchar('.');
3294           fflush(stdout);
3295         }
3296       putchar('\n');
3297     }
3298
3299 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3300   /* check for file conflicts */
3301   if (newpkgs)
3302     {
3303       Queue conflicts;
3304       struct fcstate fcstate;
3305
3306       printf("Searching for file conflicts\n");
3307       queue_init(&conflicts);
3308       fcstate.rpmstate = rpm_state_create(pool, rootdir);
3309       fcstate.newpkgscnt = newpkgs;
3310       fcstate.checkq = &checkq;
3311       fcstate.newpkgsfps = newpkgsfps;
3312       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
3313       fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
3314       if (conflicts.count)
3315         {
3316           printf("\n");
3317           for (i = 0; i < conflicts.count; i += 6)
3318             printf("file %s of package %s conflicts with package %s\n", pool_id2str(pool, conflicts.elements[i]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_solvid2str(pool, conflicts.elements[i + 4]));
3319           printf("\n");
3320           if (yesno("Re-run solver (y/n/q)? "))
3321             {
3322               for (i = 0; i < newpkgs; i++)
3323                 if (newpkgsfps[i])
3324                   fclose(newpkgsfps[i]);
3325               newpkgsfps = solv_free(newpkgsfps);
3326               solver_free(solv);
3327               solv = 0;
3328               pool_add_fileconflicts_deps(pool, &conflicts);
3329               goto rerunsolver;
3330             }
3331         }
3332       queue_free(&conflicts);
3333     }
3334 #endif
3335
3336   /* and finally commit the transaction */
3337   printf("Committing transaction:\n\n");
3338   transaction_order(trans, 0);
3339   for (i = 0; i < trans->steps.count; i++)
3340     {
3341 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3342       const char *evr, *evrp, *nvra;
3343 #endif
3344       Solvable *s;
3345       int j;
3346       FILE *fp;
3347       Id type;
3348
3349       p = trans->steps.elements[i];
3350       s = pool_id2solvable(pool, p);
3351       type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
3352       switch(type)
3353         {
3354         case SOLVER_TRANSACTION_ERASE:
3355           printf("erase %s\n", pool_solvid2str(pool, p));
3356 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3357           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
3358             continue;
3359           /* strip epoch from evr */
3360           evr = evrp = pool_id2str(pool, s->evr);
3361           while (*evrp >= '0' && *evrp <= '9')
3362             evrp++;
3363           if (evrp > evr && evrp[0] == ':' && evrp[1])
3364             evr = evrp + 1;
3365           nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
3366           nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
3367           runrpm("-e", nvra, -1, rootdir);      /* too bad that --querybynumber doesn't work */
3368 #endif
3369 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
3370           rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
3371 #endif
3372           break;
3373         case SOLVER_TRANSACTION_INSTALL:
3374         case SOLVER_TRANSACTION_MULTIINSTALL:
3375           printf("install %s\n", pool_solvid2str(pool, p));
3376           for (j = 0; j < newpkgs; j++)
3377             if (checkq.elements[j] == p)
3378               break;
3379           fp = j < newpkgs ? newpkgsfps[j] : 0;
3380           if (!fp)
3381             continue;
3382           rewind(fp);
3383           lseek(fileno(fp), 0, SEEK_SET);
3384 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3385           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
3386 #endif
3387 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
3388           rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
3389 #endif
3390           fclose(fp);
3391           newpkgsfps[j] = 0;
3392           break;
3393         default:
3394           break;
3395         }
3396     }
3397
3398   for (i = 0; i < newpkgs; i++)
3399     if (newpkgsfps[i])
3400       fclose(newpkgsfps[i]);
3401   solv_free(newpkgsfps);
3402   queue_free(&checkq);
3403   transaction_free(trans);
3404   solver_free(solv);
3405   queue_free(&job);
3406   pool_free(pool);
3407   free_repoinfos(repoinfos, nrepoinfos);
3408   solv_free(commandlinepkgs);
3409 #ifdef FEDORA
3410   yum_substitute(pool, 0);
3411 #endif
3412   exit(0);
3413 }