Imported Upstream version 0.6.12
[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   unsigned char cookie[32];
128   int extcookieset;
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)) != 0)
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       cinfo->extcookieset = 1;
1169     }
1170   if (mark)
1171     futimens(fileno(fp), 0);    /* try to set modification time */
1172   fclose(fp);
1173   return 1;
1174 }
1175
1176 void
1177 writecachedrepo(Repo *repo, Repodata *repodata, const char *repoext, unsigned char *cookie)
1178 {
1179   FILE *fp;
1180   int i, fd;
1181   char *tmpl, *cachedir;
1182   struct repoinfo *cinfo;
1183   int onepiece;
1184
1185   cinfo = repo->appdata;
1186   if (cinfo && cinfo->incomplete)
1187     return;
1188   cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
1189   if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
1190     printf("[created %s]\n", cachedir);
1191   /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
1192   tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
1193   fd = mkstemp(tmpl);
1194   if (fd < 0)
1195     {
1196       free(tmpl);
1197       return;
1198     }
1199   fchmod(fd, 0444);
1200   if (!(fp = fdopen(fd, "w")))
1201     {
1202       close(fd);
1203       unlink(tmpl);
1204       free(tmpl);
1205       return;
1206     }
1207
1208   onepiece = 1;
1209   for (i = repo->start; i < repo->end; i++)
1210    if (repo->pool->solvables[i].repo != repo)
1211      break;
1212   if (i < repo->end)
1213     onepiece = 0;
1214
1215   if (!repodata)
1216     repo_write(repo, fp);
1217   else if (repoext)
1218     repodata_write(repodata, fp);
1219   else
1220     {
1221       int oldnrepodata = repo->nrepodata;
1222       repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;    /* XXX: do this right */
1223       repo_write(repo, fp);
1224       repo->nrepodata = oldnrepodata;
1225       onepiece = 0;     /* don't bother for the added file provides */
1226     }
1227
1228   if (!repoext && cinfo)
1229     {
1230       if (!cinfo->extcookieset)
1231         {
1232           /* create the ext cookie and append it */
1233           /* we just need some unique ID */
1234           struct stat stb;
1235           if (!fstat(fileno(fp), &stb))
1236             memset(&stb, 0, sizeof(stb));
1237           calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie, cinfo->extcookie);
1238           cinfo->extcookieset = 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(repodata, repo->start, repo->end - repo->start);
1285               repodata->state = REPODATA_LOADING;
1286               if (strcmp(repoext, "DL") != 0)
1287                 flags |= REPO_LOCALPOOL;
1288               repo_add_solv(repo, fp, flags);
1289               repodata->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   if (cinfo->extcookieset)
1404     writecachedrepo(repo, data, ext, cinfo->extcookie);
1405   return 1;
1406 }
1407
1408 #endif
1409
1410
1411 #ifdef ENABLE_SUSEREPO
1412 /* susetags helpers */
1413
1414 static inline const char *
1415 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1416 {
1417   Pool *pool = repo->pool;
1418   Dataiterator di;
1419   const char *filename;
1420
1421   filename = 0;
1422   *chksump = 0;
1423   *chksumtypep = 0;
1424   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1425   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1426   if (dataiterator_step(&di))
1427     {
1428       dataiterator_setpos_parent(&di);
1429       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1430       filename = what;
1431     }
1432   dataiterator_free(&di);
1433   if (filename && !*chksumtypep)
1434     {
1435       printf("no %s file checksum!\n", what);
1436       filename = 0;
1437     }
1438   return filename;
1439 }
1440
1441 static Id susetags_langtags[] = {
1442   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1443   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1444   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1445   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1446   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1447   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1448   0, 0
1449 };
1450
1451 void
1452 susetags_add_ext(Repo *repo, Repodata *data)
1453 {
1454   Pool *pool = repo->pool;
1455   Dataiterator di;
1456   char ext[3];
1457   Id handle, filechksumtype;
1458   const unsigned char *filechksum;
1459   int i;
1460
1461   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1462   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1463   while (dataiterator_step(&di))
1464     {
1465       if (strncmp(di.kv.str, "packages.", 9) != 0)
1466         continue;
1467       if (!strcmp(di.kv.str + 9, "gz"))
1468         continue;
1469       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1470         continue;
1471       ext[0] = di.kv.str[9];
1472       ext[1] = di.kv.str[10];
1473       ext[2] = 0;
1474       if (!strcmp(ext, "en"))
1475         continue;
1476       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1477         continue;
1478       handle = repodata_new_handle(data);
1479       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1480       if (filechksumtype)
1481         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1482       if (!strcmp(ext, "DU"))
1483         {
1484           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1485           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1486         }
1487       else if (!strcmp(ext, "FL"))
1488         {
1489           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1490           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1491         }
1492       else
1493         {
1494           for (i = 0; susetags_langtags[i]; i += 2)
1495             {
1496               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1497               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1498             }
1499         }
1500       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1501     }
1502   dataiterator_free(&di);
1503 }
1504
1505 int
1506 susetags_load_ext(Repo *repo, Repodata *data)
1507 {
1508   const char *filename, *descrdir;
1509   Id defvendor;
1510   char ext[3];
1511   FILE *fp;
1512   struct repoinfo *cinfo;
1513   const unsigned char *filechksum;
1514   Id filechksumtype;
1515   int flags;
1516
1517   cinfo = repo->appdata;
1518   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1519   if (!filename)
1520     return 0;
1521   /* susetags load */
1522   ext[0] = filename[9];
1523   ext[1] = filename[10];
1524   ext[2] = 0;
1525   printf("[%s:%s", repo->name, ext);
1526   if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
1527     {
1528       printf(" cached]\n"); fflush(stdout);
1529       return 1;
1530     }
1531   printf(" fetching]\n"); fflush(stdout);
1532   defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1533   descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1534   if (!descrdir)
1535     descrdir = "suse/setup/descr";
1536   filechksumtype = 0;
1537   filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1538   if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1539     return 0;
1540   flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
1541   if (strcmp(ext, "DL") != 0)
1542     flags |= REPO_LOCALPOOL;
1543   if (repo_add_susetags(repo, fp, defvendor, ext, flags))
1544     {
1545       fclose(fp);
1546       printf("%s\n", pool_errstr(repo->pool));
1547       return 0;
1548     }
1549   fclose(fp);
1550   if (cinfo->extcookieset)
1551     writecachedrepo(repo, data, ext, cinfo->extcookie);
1552   return 1;
1553 }
1554 #endif
1555
1556
1557
1558 /* load callback */
1559
1560 int
1561 load_stub(Pool *pool, Repodata *data, void *dp)
1562 {
1563   struct repoinfo *cinfo = data->repo->appdata;
1564   switch (cinfo->type)
1565     {
1566 #ifdef ENABLE_SUSEREPO
1567     case TYPE_SUSETAGS:
1568       return susetags_load_ext(data->repo, data);
1569 #endif
1570 #ifdef ENABLE_RPMMD
1571     case TYPE_RPMMD:
1572       return repomd_load_ext(data->repo, data);
1573 #endif
1574     default:
1575       return 0;
1576     }
1577 }
1578
1579 static unsigned char installedcookie[32];
1580
1581 #ifdef ENABLE_DEBIAN
1582
1583 const char *
1584 debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
1585 {
1586   char buf[4096];
1587   Id chksumtype;
1588   unsigned char *chksum;
1589   Id curchksumtype;
1590   int l, compl;
1591   char *ch, *fn, *bp;
1592   char *filename;
1593   static char *basearch;
1594   char *binarydir;
1595   int lbinarydir;
1596
1597   if (!basearch)
1598     {
1599       struct utsname un;
1600       if (uname(&un))
1601         {
1602           perror("uname");
1603           exit(1);
1604         }
1605       basearch = strdup(un.machine);
1606       if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
1607         basearch[1] = '3';
1608     }
1609   binarydir = solv_dupjoin("binary-", basearch, "/");
1610   lbinarydir = strlen(binarydir);
1611   compl = strlen(comp);
1612   rewind(fp);
1613   curchksumtype = 0;
1614   filename = 0;
1615   chksum = solv_malloc(32);
1616   chksumtype = 0;
1617   while(fgets(buf, sizeof(buf), fp))
1618     {
1619       l = strlen(buf);
1620       if (l == 0)
1621         continue;
1622       while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
1623         buf[--l] = 0;
1624       if (!strncasecmp(buf, "MD5Sum:", 7))
1625         {
1626           curchksumtype = REPOKEY_TYPE_MD5;
1627           continue;
1628         }
1629       if (!strncasecmp(buf, "SHA1:", 5))
1630         {
1631           curchksumtype = REPOKEY_TYPE_SHA1;
1632           continue;
1633         }
1634       if (!strncasecmp(buf, "SHA256:", 7))
1635         {
1636           curchksumtype = REPOKEY_TYPE_SHA256;
1637           continue;
1638         }
1639       if (!curchksumtype)
1640         continue;
1641       bp = buf;
1642       if (*bp++ != ' ')
1643         {
1644           curchksumtype = 0;
1645           continue;
1646         }
1647       ch = bp;
1648       while (*bp && *bp != ' ' && *bp != '\t')
1649         bp++;
1650       if (!*bp)
1651         continue;
1652       *bp++ = 0;
1653       while (*bp == ' ' || *bp == '\t')
1654         bp++;
1655       while (*bp && *bp != ' ' && *bp != '\t')
1656         bp++;
1657       if (!*bp)
1658         continue;
1659       while (*bp == ' ' || *bp == '\t')
1660         bp++;
1661       fn = bp;
1662       if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
1663         continue;
1664       bp += compl + 1;
1665       if (strncmp(bp, binarydir, lbinarydir))
1666         continue;
1667       bp += lbinarydir;
1668       if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
1669         {
1670           unsigned char curchksum[32];
1671           int curl;
1672           if (filename && !strcmp(bp, "Packages"))
1673             continue;
1674           curl = solv_chksum_len(curchksumtype);
1675           if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
1676             continue;
1677           if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
1678             continue;
1679           solv_free(filename);
1680           filename = strdup(fn);
1681           chksumtype = curchksumtype;
1682           memcpy(chksum, curchksum, curl);
1683         }
1684     }
1685   free(binarydir);
1686   if (filename)
1687     {
1688       fn = solv_dupjoin("/", filename, 0);
1689       solv_free(filename);
1690       filename = solv_dupjoin("dists/", cinfo->name, fn);
1691       solv_free(fn);
1692     }
1693   if (!chksumtype)
1694     chksum = solv_free(chksum);
1695   *chksump = chksum;
1696   *chksumtypep = chksumtype;
1697   return filename;
1698 }
1699 #endif
1700
1701 void
1702 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1703 {
1704   Repo *repo;
1705   struct repoinfo *cinfo;
1706   int i;
1707   FILE *fp;
1708   const char *filename;
1709   const unsigned char *filechksum;
1710   Id filechksumtype;
1711 #ifdef ENABLE_SUSEREPO
1712   const char *descrdir;
1713   int defvendor;
1714 #endif
1715   struct stat stb;
1716   Pool *sigpool = 0;
1717 #if defined(ENABLE_SUSEREPO) || defined(ENABLE_RPMMD)
1718   Repodata *data;
1719 #endif
1720   int dorefresh;
1721 #if defined(ENABLE_DEBIAN)
1722   FILE *fpr;
1723   int j;
1724 #endif
1725
1726   repo = repo_create(pool, "@System");
1727   memset(&stb, 0, sizeof(stb));
1728 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1729   printf("rpm database:");
1730   if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
1731     memset(&stb, 0, sizeof(stb));
1732 #endif
1733 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
1734   printf("dpgk database:");
1735   if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
1736     memset(&stb, 0, sizeof(stb));
1737 #endif
1738   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, 0, installedcookie);
1739   if (usecachedrepo(repo, 0, installedcookie, 0))
1740     printf(" cached\n");
1741   else
1742     {
1743 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1744       FILE *ofp = 0;
1745 #endif
1746       printf(" reading\n");
1747
1748 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1749 # if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
1750       if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1751         {
1752           fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
1753           exit(1);
1754         }
1755 # endif
1756 # if defined(ENABLE_APPDATA)
1757       if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1758         {
1759           fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
1760           exit(1);
1761         }
1762 # endif
1763       ofp = fopen(calccachepath(repo, 0, 0), "r");
1764       if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1765         {
1766           fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
1767           exit(1);
1768         }
1769       if (ofp)
1770         fclose(ofp);
1771 #endif
1772 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
1773       if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
1774         {
1775           fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
1776           exit(1);
1777         }
1778 #endif
1779       repo_internalize(repo);
1780       writecachedrepo(repo, 0, 0, installedcookie);
1781     }
1782   pool_set_installed(pool, repo);
1783
1784   for (i = 0; i < nrepoinfos; i++)
1785     {
1786       cinfo = repoinfos + i;
1787       if (!cinfo->enabled)
1788         continue;
1789
1790       repo = repo_create(pool, cinfo->alias);
1791       cinfo->repo = repo;
1792       repo->appdata = cinfo;
1793       repo->priority = 99 - cinfo->priority;
1794
1795       dorefresh = cinfo->autorefresh;
1796       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0, 0), &stb) == 0)
1797         {
1798           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1799             dorefresh = 0;
1800         }
1801       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1802         {
1803           printf("repo '%s':", cinfo->alias);
1804           printf(" cached\n");
1805           continue;
1806         }
1807       switch (cinfo->type)
1808         {
1809 #ifdef ENABLE_RPMMD
1810         case TYPE_RPMMD:
1811           printf("rpmmd repo '%s':", cinfo->alias);
1812           fflush(stdout);
1813           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1814             {
1815               printf(" no repomd.xml file, skipped\n");
1816               repo_free(repo, 1);
1817               cinfo->repo = 0;
1818               break;
1819             }
1820           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1821           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1822             {
1823               printf(" cached\n");
1824               fclose(fp);
1825               break;
1826             }
1827           if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", &sigpool))
1828             {
1829               fclose(fp);
1830               break;
1831             }
1832           if (repo_add_repomdxml(repo, fp, 0))
1833             {
1834               printf("repomd.xml: %s\n", pool_errstr(pool));
1835               fclose(fp);
1836               break;    /* hopeless */
1837             }
1838           fclose(fp);
1839           printf(" fetching\n");
1840           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1841           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1842             {
1843               if (repo_add_rpmmd(repo, fp, 0, 0))
1844                 {
1845                   printf("primary: %s\n", pool_errstr(pool));
1846                   cinfo->incomplete = 1;
1847                 }
1848               fclose(fp);
1849             }
1850           if (cinfo->incomplete)
1851             break;      /* hopeless */
1852
1853           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1854           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1855             {
1856               if (repo_add_updateinfoxml(repo, fp, 0))
1857                 {
1858                   printf("updateinfo: %s\n", pool_errstr(pool));
1859                   cinfo->incomplete = 1;
1860                 }
1861               fclose(fp);
1862             }
1863
1864 #ifdef ENABLE_APPDATA
1865           filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
1866           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1867             {
1868               if (repo_add_appdata(repo, fp, 0))
1869                 {
1870                   printf("appdata: %s\n", pool_errstr(pool));
1871                   cinfo->incomplete = 1;
1872                 }
1873               fclose(fp);
1874             }
1875 #endif
1876           data = repo_add_repodata(repo, 0);
1877           if (!repomd_add_ext(repo, data, "deltainfo"))
1878             repomd_add_ext(repo, data, "prestodelta");
1879           repomd_add_ext(repo, data, "filelists");
1880           repodata_internalize(data);
1881           if (!cinfo->incomplete)
1882             writecachedrepo(repo, 0, 0, cinfo->cookie);
1883           repodata_create_stubs(repo_last_repodata(repo));
1884           break;
1885 #endif
1886
1887 #ifdef ENABLE_SUSEREPO
1888         case TYPE_SUSETAGS:
1889           printf("susetags repo '%s':", cinfo->alias);
1890           fflush(stdout);
1891           descrdir = 0;
1892           defvendor = 0;
1893           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1894             {
1895               printf(" no content file, skipped\n");
1896               repo_free(repo, 1);
1897               cinfo->repo = 0;
1898               break;
1899             }
1900           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1901           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1902             {
1903               printf(" cached\n");
1904               fclose(fp);
1905               break;
1906             }
1907           if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", &sigpool))
1908             {
1909               fclose(fp);
1910               break;
1911             }
1912           if (repo_add_content(repo, fp, 0))
1913             {
1914               printf("content: %s\n", pool_errstr(pool));
1915               fclose(fp);
1916               break;    /* hopeless */
1917             }
1918           fclose(fp);
1919           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1920           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1921           if (!descrdir)
1922             descrdir = "suse/setup/descr";
1923           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1924           if (!filename)
1925             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1926           if (!filename)
1927             {
1928               printf(" no packages file entry, skipped\n");
1929               break;
1930             }
1931           printf(" fetching\n");
1932           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) == 0)
1933             break;      /* hopeless */
1934           if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
1935             {
1936               printf("packages: %s\n", pool_errstr(pool));
1937               fclose(fp);
1938               cinfo->incomplete = 1;
1939               break;    /* hopeless */
1940             }
1941           fclose(fp);
1942           /* add default language */
1943           filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
1944           if (!filename)
1945             filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
1946           if (filename)
1947             {
1948               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1949                 {
1950                   if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
1951                     {
1952                       printf("packages.en: %s\n", pool_errstr(pool));
1953                       cinfo->incomplete = 1;
1954                     }
1955                   fclose(fp);
1956                 }
1957             }
1958           filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
1959           if (filename)
1960             {
1961               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1962                 {
1963                   char pbuf[256];
1964                   while (fgets(pbuf, sizeof(pbuf), fp))
1965                     {
1966                       int l = strlen(pbuf);
1967                       FILE *fp2;
1968                       if (l && pbuf[l - 1] == '\n')
1969                         pbuf[--l] = 0;
1970                       if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
1971                         continue;
1972                       filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
1973                       if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1974                         {
1975                           if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
1976                             {
1977                               printf("%s: %s\n", pbuf, pool_errstr(pool));
1978                               cinfo->incomplete = 1;
1979                             }
1980                           fclose(fp2);
1981                         }
1982                     }
1983                   fclose(fp);
1984                 }
1985             }
1986 #ifdef ENABLE_APPDATA
1987           filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
1988           if (!filename)
1989             filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
1990           if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
1991             {
1992               if (repo_add_appdata(repo, fp, 0))
1993                 {
1994                   printf("appdata: %s\n", pool_errstr(pool));
1995                   cinfo->incomplete = 1;
1996                 }
1997               fclose(fp);
1998             }
1999 #endif
2000           repo_internalize(repo);
2001           data = repo_add_repodata(repo, 0);
2002           susetags_add_ext(repo, data);
2003           repodata_internalize(data);
2004           if (!cinfo->incomplete)
2005             writecachedrepo(repo, 0, 0, cinfo->cookie);
2006           repodata_create_stubs(repo_last_repodata(repo));
2007           break;
2008 #endif
2009
2010 #if defined(ENABLE_DEBIAN)
2011         case TYPE_DEBIAN:
2012           printf("debian repo '%s':", cinfo->alias);
2013           fflush(stdout);
2014           filename = solv_dupjoin("dists/", cinfo->name, "/Release");
2015           if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
2016             {
2017               printf(" no Release file, skipped\n");
2018               repo_free(repo, 1);
2019               cinfo->repo = 0;
2020               free((char *)filename);
2021               break;
2022             }
2023           solv_free((char *)filename);
2024           if (cinfo->repo_gpgcheck)
2025             {
2026               filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
2027               if (!downloadchecksig(cinfo, fpr, filename, &sigpool))
2028                 {
2029                   fclose(fpr);
2030                   solv_free((char *)filename);
2031                   break;
2032                 }
2033               solv_free((char *)filename);
2034             }
2035           calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
2036           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
2037             {
2038               printf(" cached\n");
2039               fclose(fpr);
2040               break;
2041             }
2042           printf(" fetching\n");
2043           for (j = 0; j < cinfo->ncomponents; j++)
2044             {
2045               if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
2046                 {
2047                   printf("[component %s not found]\n", cinfo->components[j]);
2048                   continue;
2049                 }
2050               if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
2051                 {
2052                   if (repo_add_debpackages(repo, fp, 0))
2053                     {
2054                       printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
2055                       cinfo->incomplete = 1;
2056                     }
2057                   fclose(fp);
2058                 }
2059               solv_free((char *)filechksum);
2060               solv_free((char *)filename);
2061             }
2062           fclose(fpr);
2063           if (!cinfo->incomplete)
2064             writecachedrepo(repo, 0, 0, cinfo->cookie);
2065           break;
2066 #endif
2067
2068         default:
2069           printf("unsupported repo '%s': skipped\n", cinfo->alias);
2070           repo_free(repo, 1);
2071           cinfo->repo = 0;
2072           break;
2073         }
2074     }
2075   if (sigpool)
2076     pool_free(sigpool);
2077 }
2078
2079 int
2080 yesno(const char *str)
2081 {
2082   char inbuf[128], *ip;
2083
2084   for (;;)
2085     {
2086       printf("%s", str);
2087       fflush(stdout);
2088       *inbuf = 0;
2089       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2090         {
2091           printf("Abort.\n");
2092           exit(1);
2093         }
2094       while (*ip == ' ' || *ip == '\t')
2095         ip++;
2096       if (*ip == 'q')
2097         {
2098           printf("Abort.\n");
2099           exit(1);
2100         }
2101       if (*ip == 'y' || *ip == 'n')
2102         return *ip == 'y' ? 1 : 0;
2103     }
2104 }
2105
2106 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2107
2108 struct fcstate {
2109   FILE **newpkgsfps;
2110   Queue *checkq;
2111   int newpkgscnt;
2112   void *rpmstate;
2113 };
2114
2115 static void *
2116 fileconflict_cb(Pool *pool, Id p, void *cbdata)
2117 {
2118   struct fcstate *fcstate = cbdata;
2119   Solvable *s;
2120   Id rpmdbid;
2121   int i;
2122   FILE *fp;
2123
2124   s = pool_id2solvable(pool, p);
2125   if (pool->installed && s->repo == pool->installed)
2126     {
2127       if (!s->repo->rpmdbid)
2128         return 0;
2129       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
2130       if (!rpmdbid)
2131         return 0;
2132       return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
2133     }
2134   for (i = 0; i < fcstate->newpkgscnt; i++)
2135     if (fcstate->checkq->elements[i] == p)
2136       break;
2137   if (i == fcstate->newpkgscnt)
2138     return 0;
2139   fp = fcstate->newpkgsfps[i];
2140   if (!fp)
2141     return 0;
2142   rewind(fp);
2143   return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
2144 }
2145
2146
2147 void
2148 runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
2149 {
2150   pid_t pid;
2151   int status;
2152
2153   if ((pid = fork()) == (pid_t)-1)
2154     {
2155       perror("fork");
2156       exit(1);
2157     }
2158   if (pid == 0)
2159     {
2160       if (!rootdir)
2161         rootdir = "/";
2162       if (dupfd3 != -1 && dupfd3 != 3)
2163         {
2164           dup2(dupfd3, 3);
2165           close(dupfd3);
2166         }
2167       if (dupfd3 != -1)
2168         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2169       if (strcmp(arg, "-e") == 0)
2170         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
2171       else
2172         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
2173       perror("rpm");
2174       _exit(0);
2175     }
2176   while (waitpid(pid, &status, 0) != pid)
2177     ;
2178   if (status)
2179     {
2180       printf("rpm failed\n");
2181       exit(1);
2182     }
2183 }
2184
2185 #endif
2186
2187 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2188
2189 void
2190 rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
2191 {
2192   pid_t pid;
2193   int status;
2194
2195   if ((pid = fork()) == (pid_t)-1)
2196     {
2197       perror("fork");
2198       exit(1);
2199     }
2200   if (pid == 0)
2201     {
2202       if (!rootdir)
2203         rootdir = "/";
2204       if (dupfd3 != -1 && dupfd3 != 3)
2205         {
2206           dup2(dupfd3, 3);
2207           close(dupfd3);
2208         }
2209       if (dupfd3 != -1)
2210         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2211       if (strcmp(arg, "--install") == 0)
2212         execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
2213       else
2214         execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
2215       perror("dpkg");
2216       _exit(0);
2217     }
2218   while (waitpid(pid, &status, 0) != pid)
2219     ;
2220   if (status)
2221     {
2222       printf("dpkg failed\n");
2223       exit(1);
2224     }
2225 }
2226
2227 #endif
2228
2229 #ifdef SUSE
2230 static Id
2231 nscallback(Pool *pool, void *data, Id name, Id evr)
2232 {
2233 #if 0
2234   if (name == NAMESPACE_LANGUAGE)
2235     {
2236       if (!strcmp(pool_id2str(pool, evr), "ja"))
2237         return 1;
2238       if (!strcmp(pool_id2str(pool, evr), "de"))
2239         return 1;
2240       if (!strcmp(pool_id2str(pool, evr), "en"))
2241         return 1;
2242       if (!strcmp(pool_id2str(pool, evr), "en_US"))
2243         return 1;
2244     }
2245 #endif
2246   return 0;
2247 }
2248 #endif
2249
2250 #ifdef SOFTLOCKS_PATH
2251 void
2252 addsoftlocks(Pool *pool, Queue *job)
2253 {
2254   FILE *fp;
2255   Id type, id, p, pp;
2256   char *bp, *ep, buf[4096];
2257
2258   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2259     return;
2260   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2261     {
2262       while (*bp == ' ' || *bp == '\t')
2263         bp++;
2264       if (!*bp || *bp == '#')
2265         continue;
2266       for (ep = bp; *ep; ep++)
2267         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2268           break;
2269       *ep = 0;
2270       type = SOLVER_SOLVABLE_NAME;
2271       if (!strncmp(bp, "provides:", 9) && bp[9])
2272         {
2273           type = SOLVER_SOLVABLE_PROVIDES;
2274           bp += 9;
2275         }
2276       id = pool_str2id(pool, bp, 1);
2277       if (pool->installed)
2278         {
2279           FOR_JOB_SELECT(p, pp, type, id)
2280             if (pool->solvables[p].repo == pool->installed)
2281               break;
2282           if (p)
2283             continue;   /* ignore, as it is already installed */
2284         }
2285       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2286     }
2287   fclose(fp);
2288 }
2289 #endif
2290
2291
2292 #if defined(ENABLE_RPMDB)
2293
2294 static void
2295 rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
2296 {
2297   Repo *repo;
2298   Repodata *data;
2299   Map providedids;
2300   Queue fileprovidesq;
2301   int i, j, n;
2302   struct repoinfo *cinfo;
2303
2304   map_init(&providedids, pool->ss.nstrings);
2305   queue_init(&fileprovidesq);
2306   for (i = 0; i < addedfileprovides->count; i++)
2307     MAPSET(&providedids, addedfileprovides->elements[i]);
2308   FOR_REPOS(i, repo)
2309     {
2310       /* make sure all repodatas but the first are extensions */
2311       if (repo->nrepodata < 2)
2312         continue;
2313       cinfo = repo->appdata;
2314       if (repo != pool->installed && !cinfo)
2315         continue;
2316       if (cinfo && cinfo->incomplete)
2317         continue;
2318       data = repo_id2repodata(repo, 1);
2319       if (data->loadcallback)
2320         continue;
2321       for (j = 2; j < repo->nrepodata; j++)
2322         {
2323           Repodata *edata = repo_id2repodata(repo, j);
2324           if (!edata->loadcallback)
2325             break;
2326         }
2327       if (j < repo->nrepodata)
2328         continue;       /* found a non-externsion repodata, can't rewrite  */
2329       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2330         {
2331           if (repo == pool->installed && addedfileprovides_inst)
2332             {
2333               for (j = 0; j < addedfileprovides->count; j++)
2334                 MAPCLR(&providedids, addedfileprovides->elements[j]);
2335               for (j = 0; j < addedfileprovides_inst->count; j++)
2336                 MAPSET(&providedids, addedfileprovides_inst->elements[j]);
2337             }
2338           n = 0;
2339           for (j = 0; j < fileprovidesq.count; j++)
2340             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2341               n++;
2342           if (repo == pool->installed && addedfileprovides_inst)
2343             {
2344               for (j = 0; j < addedfileprovides_inst->count; j++)
2345                 MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
2346               for (j = 0; j < addedfileprovides->count; j++)
2347                 MAPSET(&providedids, addedfileprovides->elements[j]);
2348               if (n == addedfileprovides_inst->count)
2349                 continue;       /* nothing new added */
2350             }
2351           else if (n == addedfileprovides->count)
2352             continue;   /* nothing new added */
2353         }
2354       repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
2355       repodata_internalize(data);
2356       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2357     }
2358   queue_free(&fileprovidesq);
2359   map_free(&providedids);
2360 }
2361
2362 static void
2363 addfileprovides(Pool *pool)
2364 {
2365   Queue addedfileprovides;
2366   Queue addedfileprovides_inst;
2367
2368   queue_init(&addedfileprovides);
2369   queue_init(&addedfileprovides_inst);
2370   pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
2371   if (addedfileprovides.count || addedfileprovides_inst.count)
2372     rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
2373   queue_free(&addedfileprovides);
2374   queue_free(&addedfileprovides_inst);
2375 }
2376
2377 #endif
2378
2379 #ifdef SUSE
2380 static void
2381 add_autopackages(Pool *pool)
2382 {
2383   int i;
2384   Repo *repo;
2385   FOR_REPOS(i, repo)
2386     repo_add_autopattern(repo, 0);
2387 }
2388 #endif
2389
2390 #if defined(SUSE) || defined(FEDORA)
2391 static void
2392 add_patchjobs(Pool *pool, Queue *job)
2393 {
2394   Id p, pp;
2395   int pruneyou = 0;
2396   Map installedmap, multiversionmap;
2397   Solvable *s;
2398
2399   map_init(&multiversionmap, 0);
2400   map_init(&installedmap, pool->nsolvables);
2401   solver_calculate_multiversionmap(pool, job, &multiversionmap);
2402   if (pool->installed)
2403     FOR_REPO_SOLVABLES(pool->installed, p, s)
2404       MAPSET(&installedmap, p);
2405
2406   /* install all patches */
2407   for (p = 1; p < pool->nsolvables; p++)
2408     {
2409       const char *type;
2410       int r;
2411       Id p2;
2412
2413       s = pool->solvables + p;
2414       if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
2415         continue;
2416       FOR_PROVIDES(p2, pp, s->name)
2417         {
2418           Solvable *s2 = pool->solvables + p2;
2419           if (s2->name != s->name)
2420             continue;
2421           r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2422           if (r < 0 || (r == 0 && p > p2))
2423             break;
2424         }
2425       if (p2)
2426         continue;
2427       type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2428       if (type && !strcmp(type, "optional"))
2429         continue;
2430       r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
2431       if (r == -1)
2432         continue;
2433       if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2434         {
2435           if (!pruneyou++)
2436             queue_empty(job);
2437         }
2438       else if (pruneyou)
2439         continue;
2440       queue_push2(job, SOLVER_SOLVABLE, p);
2441     }
2442   map_free(&installedmap);
2443   map_free(&multiversionmap);
2444 }
2445 #endif
2446
2447 #ifdef SUSE
2448 static void
2449 showdiskusagechanges(Transaction *trans)
2450 {
2451   DUChanges duc[4];
2452   int i;
2453
2454   /* XXX: use mountpoints here */
2455   memset(duc, 0, sizeof(duc));
2456   duc[0].path = "/";
2457   duc[1].path = "/usr/share/man";
2458   duc[2].path = "/sbin";
2459   duc[3].path = "/etc";
2460   transaction_calc_duchanges(trans, duc, 4);
2461   for (i = 0; i < 4; i++)
2462     printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2463 }
2464 #endif
2465
2466 #if defined(ENABLE_RPMDB)
2467 static FILE *
2468 trydeltadownload(Solvable *s, struct repoinfo *cinfo, const char *loc)
2469 {
2470   Pool *pool = s->repo->pool;
2471   Dataiterator di;
2472   Id pp;
2473   const unsigned char *chksum;
2474   Id chksumtype;
2475   FILE *retfp = 0;
2476   char *matchname = strdup(pool_id2str(pool, s->name));
2477
2478   dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2479   dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2480   while (dataiterator_step(&di))
2481     {
2482       Id baseevr, op;
2483
2484       dataiterator_setpos_parent(&di);
2485       if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2486           pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2487         continue;
2488       baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2489       FOR_PROVIDES(op, pp, s->name)
2490         {
2491           Solvable *os = pool->solvables + op;
2492           if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2493             break;
2494         }
2495       if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2496         {
2497           /* base is installed, run sequence check */
2498           const char *seq;
2499           const char *dloc;
2500           const char *archstr;
2501           FILE *fp;
2502           char cmd[128];
2503           int newfd;
2504
2505           archstr = pool_id2str(pool, s->arch);
2506           if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
2507             continue;
2508
2509           seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
2510           seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
2511           if (strchr(seq, '\'') != 0)
2512             continue;
2513 #ifdef FEDORA
2514           sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
2515 #else
2516           sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
2517 #endif
2518           if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
2519             continue;   /* didn't match */
2520           /* looks good, download delta */
2521           chksumtype = 0;
2522           chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2523           if (!chksumtype)
2524             continue;   /* no way! */
2525           dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
2526           if (!dloc)
2527             continue;
2528 #ifdef ENABLE_SUSEREPO
2529           if (cinfo->type == TYPE_SUSETAGS)
2530             {
2531               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2532               dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
2533             }
2534 #endif
2535           if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2536             continue;
2537           /* got it, now reconstruct */
2538           newfd = opentmpfile();
2539 #ifdef FEDORA
2540           sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
2541 #else
2542           sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2543 #endif
2544           fcntl(fileno(fp), F_SETFD, 0);
2545           if (system(cmd))
2546             {
2547               close(newfd);
2548               fclose(fp);
2549               continue;
2550             }
2551           lseek(newfd, 0, SEEK_SET);
2552           chksumtype = 0;
2553           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2554           if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2555             {
2556               close(newfd);
2557               fclose(fp);
2558               continue;
2559             }
2560           retfp = fdopen(newfd, "r");
2561           fclose(fp);
2562           break;
2563         }
2564     }
2565   dataiterator_free(&di);
2566   solv_free(matchname);
2567   return retfp;
2568 }
2569 #endif
2570
2571
2572 #define MODE_LIST        0
2573 #define MODE_INSTALL     1
2574 #define MODE_ERASE       2
2575 #define MODE_UPDATE      3
2576 #define MODE_DISTUPGRADE 4
2577 #define MODE_VERIFY      5
2578 #define MODE_PATCH       6
2579 #define MODE_INFO        7
2580 #define MODE_REPOLIST    8
2581 #define MODE_SEARCH      9
2582
2583 void
2584 usage(int r)
2585 {
2586   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2587   fprintf(stderr, "\n");
2588   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2589   fprintf(stderr, "                  versions from the repositories\n");
2590   fprintf(stderr, "    erase:        erase installed packages\n");
2591   fprintf(stderr, "    info:         display package information\n");
2592   fprintf(stderr, "    install:      install packages\n");
2593   fprintf(stderr, "    list:         list packages\n");
2594   fprintf(stderr, "    repos:        list enabled repositories\n");
2595   fprintf(stderr, "    search:       search name/summary/description\n");
2596   fprintf(stderr, "    update:       update installed packages\n");
2597   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2598 #if defined(SUSE) || defined(FEDORA)
2599   fprintf(stderr, "    patch:        install newest patches\n");
2600 #endif
2601   fprintf(stderr, "\n");
2602   exit(r);
2603 }
2604
2605 int
2606 main(int argc, char **argv)
2607 {
2608   Pool *pool;
2609   Repo *commandlinerepo = 0;
2610   Id *commandlinepkgs = 0;
2611   Id p;
2612   struct repoinfo *repoinfos;
2613   int nrepoinfos = 0;
2614   int mainmode = 0, mode = 0;
2615   int i, newpkgs;
2616   Queue job, checkq;
2617   Solver *solv = 0;
2618   Transaction *trans;
2619   FILE **newpkgsfps;
2620   Queue repofilter;
2621   Queue kindfilter;
2622   Queue archfilter;
2623   int archfilter_src = 0;
2624   int cleandeps = 0;
2625   int forcebest = 0;
2626   char *rootdir = 0;
2627   char *keyname = 0;
2628   int debuglevel = 0;
2629
2630   argc--;
2631   argv++;
2632   userhome = getenv("HOME");
2633   if (userhome && userhome[0] != '/')
2634     userhome = 0;
2635   while (argc && !strcmp(argv[0], "-d"))
2636     {
2637       debuglevel++;
2638       argc--;
2639       argv++;
2640     }
2641   if (!argv[0])
2642     usage(1);
2643   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2644     {
2645       mainmode = MODE_INSTALL;
2646       mode = SOLVER_INSTALL;
2647     }
2648 #if defined(SUSE) || defined(FEDORA)
2649   else if (!strcmp(argv[0], "patch"))
2650     {
2651       mainmode = MODE_PATCH;
2652       mode = SOLVER_INSTALL;
2653     }
2654 #endif
2655   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2656     {
2657       mainmode = MODE_ERASE;
2658       mode = SOLVER_ERASE;
2659     }
2660   else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
2661     {
2662       mainmode = MODE_LIST;
2663       mode = 0;
2664     }
2665   else if (!strcmp(argv[0], "info"))
2666     {
2667       mainmode = MODE_INFO;
2668       mode = 0;
2669     }
2670   else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
2671     {
2672       mainmode = MODE_SEARCH;
2673       mode = 0;
2674     }
2675   else if (!strcmp(argv[0], "verify"))
2676     {
2677       mainmode = MODE_VERIFY;
2678       mode = SOLVER_VERIFY;
2679     }
2680   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2681     {
2682       mainmode = MODE_UPDATE;
2683       mode = SOLVER_UPDATE;
2684     }
2685   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2686     {
2687       mainmode = MODE_DISTUPGRADE;
2688       mode = SOLVER_DISTUPGRADE;
2689     }
2690   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2691     {
2692       mainmode = MODE_REPOLIST;
2693       mode = 0;
2694     }
2695   else
2696     usage(1);
2697
2698   for (;;)
2699     {
2700       if (argc > 2 && !strcmp(argv[1], "--root"))
2701         {
2702           rootdir = argv[2];
2703           argc -= 2;
2704           argv += 2;
2705         }
2706       else if (argc > 1 && !strcmp(argv[1], "--clean"))
2707         {
2708           cleandeps = 1;
2709           argc--;
2710           argv++;
2711         }
2712       else if (argc > 1 && !strcmp(argv[1], "--best"))
2713         {
2714           forcebest = 1;
2715           argc--;
2716           argv++;
2717         }
2718       if (argc > 2 && !strcmp(argv[1], "--keyname"))
2719         {
2720           keyname = argv[2];
2721           argc -= 2;
2722           argv += 2;
2723         }
2724       else
2725         break;
2726     }
2727
2728   pool = pool_create();
2729   pool_set_rootdir(pool, rootdir);
2730
2731 #if 0
2732   {
2733     const char *langs[] = {"de_DE", "de", "en"};
2734     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
2735   }
2736 #endif
2737
2738   pool_setloadcallback(pool, load_stub, 0);
2739 #ifdef SUSE
2740   pool->nscallback = nscallback;
2741 #endif
2742   if (debuglevel)
2743     pool_setdebuglevel(pool, debuglevel);
2744   setarch(pool);
2745   pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
2746   repoinfos = read_repoinfos(pool, &nrepoinfos);
2747
2748   if (mainmode == MODE_REPOLIST)
2749     {
2750       int j = 1;
2751       for (i = 0; i < nrepoinfos; i++)
2752         {
2753           struct repoinfo *cinfo = repoinfos + i;
2754           if (!cinfo->enabled)
2755             continue;
2756           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
2757         }
2758       exit(0);
2759     }
2760
2761   read_repos(pool, repoinfos, nrepoinfos);
2762
2763   /* setup filters */
2764   queue_init(&repofilter);
2765   queue_init(&kindfilter);
2766   queue_init(&archfilter);
2767   while (argc > 1)
2768     {
2769       if (!strcmp(argv[1], "-i"))
2770         {
2771           queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
2772           argc--;
2773           argv++;
2774         }
2775       else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
2776         {
2777           const char *rname = argv[2], *rp;
2778           Id repoid = 0;
2779           for (rp = rname; *rp; rp++)
2780             if (*rp <= '0' || *rp >= '9')
2781               break;
2782           if (!*rp)
2783             {
2784               /* repo specified by number */
2785               int rnum = atoi(rname);
2786               for (i = 0; i < nrepoinfos; i++)
2787                 {
2788                   struct repoinfo *cinfo = repoinfos + i;
2789                   if (!cinfo->enabled)
2790                     continue;
2791                   if (--rnum == 0)
2792                     repoid = cinfo->repo->repoid;
2793                 }
2794             }
2795           else
2796             {
2797               /* repo specified by alias */
2798               Repo *repo;
2799               FOR_REPOS(i, repo)
2800                 {
2801                   if (!strcasecmp(rname, repo->name))
2802                     repoid = repo->repoid;
2803                 }
2804             }
2805           if (!repoid)
2806             {
2807               fprintf(stderr, "%s: no such repo\n", rname);
2808               exit(1);
2809             }
2810           /* SETVENDOR is actually wrong but useful */
2811           queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
2812           argc -= 2;
2813           argv += 2;
2814         }
2815       else if (argc > 2 && !strcmp(argv[1], "--arch"))
2816         {
2817           if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
2818             archfilter_src = 1;
2819           queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
2820           argc -= 2;
2821           argv += 2;
2822         }
2823       else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
2824         {
2825           const char *kind = argv[2];
2826           if (!strcmp(kind, "srcpackage"))
2827             {
2828               /* hey! should use --arch! */
2829               queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
2830               archfilter_src = 1;
2831               argc -= 2;
2832               argv += 2;
2833               continue;
2834             }
2835           if (!strcmp(kind, "package"))
2836             kind = "";
2837           if (!strcmp(kind, "all"))
2838             queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
2839           else
2840             queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
2841           argc -= 2;
2842           argv += 2;
2843         }
2844       else
2845         break;
2846     }
2847
2848   if (mainmode == MODE_SEARCH)
2849     {
2850       Queue sel, q;
2851       Dataiterator di;
2852       if (argc != 2)
2853         usage(1);
2854       pool_createwhatprovides(pool);
2855       queue_init(&sel);
2856       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2857       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2858       dataiterator_set_search(&di, 0, 0);
2859       while (dataiterator_step(&di))
2860         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
2861       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2862       dataiterator_set_search(&di, 0, 0);
2863       while (dataiterator_step(&di))
2864         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
2865       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2866       dataiterator_set_search(&di, 0, 0);
2867       while (dataiterator_step(&di))
2868         queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
2869       dataiterator_free(&di);
2870       if (repofilter.count)
2871         selection_filter(pool, &sel, &repofilter);
2872         
2873       queue_init(&q);
2874       selection_solvables(pool, &sel, &q);
2875       queue_free(&sel);
2876       for (i = 0; i < q.count; i++)
2877         {
2878           Solvable *s = pool_id2solvable(pool, q.elements[i]);
2879           printf("  - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
2880         }
2881       queue_free(&q);
2882       exit(0);
2883     }
2884
2885   /* process command line packages */
2886   if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
2887     {
2888       for (i = 1; i < argc; i++)
2889         {
2890           int l;
2891           l = strlen(argv[i]);
2892 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2893           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2894             continue;
2895 #endif
2896 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2897           if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
2898             continue;
2899 #endif
2900           if (access(argv[i], R_OK))
2901             {
2902               perror(argv[i]);
2903               exit(1);
2904             }
2905           if (!commandlinepkgs)
2906             commandlinepkgs = solv_calloc(argc, sizeof(Id));
2907           if (!commandlinerepo)
2908             commandlinerepo = repo_create(pool, "@commandline");
2909           p = 0;
2910 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2911           p = repo_add_rpm(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2912 #endif
2913 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2914           p = repo_add_deb(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2915 #endif
2916           if (!p)
2917             {
2918               fprintf(stderr, "could not add '%s'\n", argv[i]);
2919               exit(1);
2920             }
2921           commandlinepkgs[i] = p;
2922         }
2923       if (commandlinerepo)
2924         repo_internalize(commandlinerepo);
2925     }
2926
2927   // FOR_REPOS(i, repo)
2928   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2929
2930 #if defined(ENABLE_RPMDB)
2931   if (pool->disttype == DISTTYPE_RPM)
2932     addfileprovides(pool);
2933 #endif
2934 #ifdef SUSE
2935   add_autopackages(pool);
2936 #endif
2937   pool_createwhatprovides(pool);
2938
2939   if (keyname)
2940     keyname = solv_dupjoin("solvable:", keyname, 0);
2941   queue_init(&job);
2942   for (i = 1; i < argc; i++)
2943     {
2944       Queue job2;
2945       int j, flags, rflags;
2946
2947       if (commandlinepkgs && commandlinepkgs[i])
2948         {
2949           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2950           continue;
2951         }
2952       queue_init(&job2);
2953       flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
2954       flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
2955       if (kindfilter.count)
2956         flags |= SELECTION_SKIP_KIND;
2957       if (mode == MODE_LIST || archfilter_src)
2958         flags |= SELECTION_WITH_SOURCE;
2959       if (argv[i][0] == '/')
2960         flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
2961       if (!keyname)
2962         rflags = selection_make(pool, &job2, argv[i], flags);
2963       else
2964         rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
2965       if (repofilter.count)
2966         selection_filter(pool, &job2, &repofilter);
2967       if (archfilter.count)
2968         selection_filter(pool, &job2, &archfilter);
2969       if (kindfilter.count)
2970         selection_filter(pool, &job2, &kindfilter);
2971       if (!job2.count)
2972         {
2973           flags |= SELECTION_NOCASE;
2974           if (!keyname)
2975             rflags = selection_make(pool, &job2, argv[i], flags);
2976           else
2977             rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
2978           if (repofilter.count)
2979             selection_filter(pool, &job2, &repofilter);
2980           if (archfilter.count)
2981             selection_filter(pool, &job2, &archfilter);
2982           if (kindfilter.count)
2983             selection_filter(pool, &job2, &kindfilter);
2984           if (job2.count)
2985             printf("[ignoring case for '%s']\n", argv[i]);
2986         }
2987       if (!job2.count)
2988         {
2989           fprintf(stderr, "nothing matches '%s'\n", argv[i]);
2990           exit(1);
2991         }
2992       if (rflags & SELECTION_FILELIST)
2993         printf("[using file list match for '%s']\n", argv[i]);
2994       if (rflags & SELECTION_PROVIDES)
2995         printf("[using capability match for '%s']\n", argv[i]);
2996       for (j = 0; j < job2.count; j++)
2997         queue_push(&job, job2.elements[j]);
2998       queue_free(&job2);
2999     }
3000   keyname = solv_free(keyname);
3001
3002   if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
3003     {
3004       queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
3005       if (repofilter.count)
3006         selection_filter(pool, &job, &repofilter);
3007       if (archfilter.count)
3008         selection_filter(pool, &job, &archfilter);
3009       if (kindfilter.count)
3010         selection_filter(pool, &job, &kindfilter);
3011     }
3012   queue_free(&repofilter);
3013   queue_free(&archfilter);
3014   queue_free(&kindfilter);
3015
3016   if (!job.count && mainmode != MODE_PATCH)
3017     {
3018       printf("no package matched\n");
3019       exit(1);
3020     }
3021
3022   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
3023     {
3024       /* list mode, no solver needed */
3025       Queue q;
3026       queue_init(&q);
3027       for (i = 0; i < job.count; i += 2)
3028         {
3029           int j;
3030           queue_empty(&q);
3031           pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
3032           for (j = 0; j < q.count; j++)
3033             {
3034               Solvable *s = pool_id2solvable(pool, q.elements[j]);
3035               if (mainmode == MODE_INFO)
3036                 {
3037                   const char *str;
3038                   printf("Name:        %s\n", pool_solvable2str(pool, s));
3039                   printf("Repo:        %s\n", s->repo->name);
3040                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
3041                   str = solvable_lookup_str(s, SOLVABLE_URL);
3042                   if (str)
3043                     printf("Url:         %s\n", str);
3044                   str = solvable_lookup_str(s, SOLVABLE_LICENSE);
3045                   if (str)
3046                     printf("License:     %s\n", str);
3047 #if 0
3048                   str = solvable_lookup_sourcepkg(s);
3049                   if (str)
3050                     printf("Source:      %s\n", str);
3051 #endif
3052                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
3053                   printf("\n");
3054                 }
3055               else
3056                 {
3057 #if 1
3058                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
3059 #else
3060                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
3061 #endif
3062                   printf("  - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
3063                   if (sum)
3064                     printf("    %s\n", sum);
3065                 }
3066             }
3067         }
3068       queue_free(&q);
3069       queue_free(&job);
3070       pool_free(pool);
3071       free_repoinfos(repoinfos, nrepoinfos);
3072       solv_free(commandlinepkgs);
3073 #ifdef FEDORA
3074       yum_substitute(pool, 0);
3075 #endif
3076       exit(0);
3077     }
3078
3079 #if defined(SUSE) || defined(FEDORA)
3080   if (mainmode == MODE_PATCH)
3081     add_patchjobs(pool, &job);
3082 #endif
3083
3084   // add mode
3085   for (i = 0; i < job.count; i += 2)
3086     {
3087       job.elements[i] |= mode;
3088       if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
3089         job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
3090       if (cleandeps)
3091         job.elements[i] |= SOLVER_CLEANDEPS;
3092       if (forcebest)
3093         job.elements[i] |= SOLVER_FORCEBEST;
3094     }
3095
3096   // multiversion test
3097   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
3098   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
3099   // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
3100 #if 0
3101   queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
3102   queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
3103 #endif
3104
3105 #ifdef SOFTLOCKS_PATH
3106   addsoftlocks(pool, &job);
3107 #endif
3108
3109 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3110 rerunsolver:
3111 #endif
3112   solv = solver_create(pool);
3113   solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
3114 #ifdef FEDORA
3115   solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
3116 #endif
3117   if (mainmode == MODE_ERASE)
3118     solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);      /* don't nag */
3119   solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
3120
3121   for (;;)
3122     {
3123       Id problem, solution;
3124       int pcnt, scnt;
3125
3126       if (!solver_solve(solv, &job))
3127         break;
3128       pcnt = solver_problem_count(solv);
3129       printf("Found %d problems:\n", pcnt);
3130       for (problem = 1; problem <= pcnt; problem++)
3131         {
3132           int take = 0;
3133           printf("Problem %d/%d:\n", problem, pcnt);
3134           solver_printprobleminfo(solv, problem);
3135           printf("\n");
3136           scnt = solver_solution_count(solv, problem);
3137           for (solution = 1; solution <= scnt; solution++)
3138             {
3139               printf("Solution %d:\n", solution);
3140               solver_printsolution(solv, problem, solution);
3141               printf("\n");
3142             }
3143           for (;;)
3144             {
3145               char inbuf[128], *ip;
3146               printf("Please choose a solution: ");
3147               fflush(stdout);
3148               *inbuf = 0;
3149               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
3150                 {
3151                   printf("Abort.\n");
3152                   exit(1);
3153                 }
3154               while (*ip == ' ' || *ip == '\t')
3155                 ip++;
3156               if (*ip >= '0' && *ip <= '9')
3157                 {
3158                   take = atoi(ip);
3159                   if (take >= 1 && take <= scnt)
3160                     break;
3161                 }
3162               if (*ip == 's')
3163                 {
3164                   take = 0;
3165                   break;
3166                 }
3167               if (*ip == 'q')
3168                 {
3169                   printf("Abort.\n");
3170                   exit(1);
3171                 }
3172             }
3173           if (!take)
3174             continue;
3175           solver_take_solution(solv, problem, take, &job);
3176         }
3177     }
3178
3179   trans = solver_create_transaction(solv);
3180   if (!trans->steps.count)
3181     {
3182       printf("Nothing to do.\n");
3183       transaction_free(trans);
3184       solver_free(solv);
3185       queue_free(&job);
3186       pool_free(pool);
3187       free_repoinfos(repoinfos, nrepoinfos);
3188       solv_free(commandlinepkgs);
3189 #ifdef FEDORA
3190       yum_substitute(pool, 0);
3191 #endif
3192       exit(1);
3193     }
3194
3195   /* display transaction to the user and ask for confirmation */
3196   printf("\n");
3197   printf("Transaction summary:\n\n");
3198   transaction_print(trans);
3199 #if defined(SUSE)
3200   showdiskusagechanges(trans);
3201 #endif
3202   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
3203   printf("\n");
3204
3205   if (!yesno("OK to continue (y/n)? "))
3206     {
3207       printf("Abort.\n");
3208       transaction_free(trans);
3209       solver_free(solv);
3210       queue_free(&job);
3211       pool_free(pool);
3212       free_repoinfos(repoinfos, nrepoinfos);
3213       solv_free(commandlinepkgs);
3214 #ifdef FEDORA
3215       yum_substitute(pool, 0);
3216 #endif
3217       exit(1);
3218     }
3219
3220   /* download all new packages */
3221   queue_init(&checkq);
3222   newpkgs = transaction_installedresult(trans, &checkq);
3223   newpkgsfps = 0;
3224   if (newpkgs)
3225     {
3226       int downloadsize = 0;
3227       for (i = 0; i < newpkgs; i++)
3228         {
3229           Solvable *s;
3230
3231           p = checkq.elements[i];
3232           s = pool_id2solvable(pool, p);
3233           downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
3234         }
3235       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
3236       newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
3237       for (i = 0; i < newpkgs; i++)
3238         {
3239           unsigned int medianr;
3240           const char *loc;
3241           Solvable *s;
3242           struct repoinfo *cinfo;
3243           const unsigned char *chksum;
3244           Id chksumtype;
3245
3246           p = checkq.elements[i];
3247           s = pool_id2solvable(pool, p);
3248           if (s->repo == commandlinerepo)
3249             {
3250               loc = solvable_lookup_location(s, &medianr);
3251               if (!loc)
3252                 continue;
3253               if (!(newpkgsfps[i] = fopen(loc, "r")))
3254                 {
3255                   perror(loc);
3256                   exit(1);
3257                 }
3258               putchar('.');
3259               continue;
3260             }
3261           cinfo = s->repo->appdata;
3262           if (!cinfo)
3263             {
3264               printf("%s: no repository information\n", s->repo->name);
3265               exit(1);
3266             }
3267           loc = solvable_lookup_location(s, &medianr);
3268           if (!loc)
3269              continue;
3270 #if defined(ENABLE_RPMDB)
3271           if (pool->installed && pool->installed->nsolvables)
3272             {
3273               if ((newpkgsfps[i] = trydeltadownload(s, cinfo, loc)) != 0)
3274                 {
3275                   putchar('d');
3276                   fflush(stdout);
3277                   continue;             /* delta worked! */
3278                 }
3279             }
3280 #endif
3281 #ifdef ENABLE_SUSEREPO
3282           if (cinfo->type == TYPE_SUSETAGS)
3283             {
3284               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
3285               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
3286             }
3287 #endif
3288           chksumtype = 0;
3289           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3290           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
3291             {
3292               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
3293               exit(1);
3294             }
3295           putchar('.');
3296           fflush(stdout);
3297         }
3298       putchar('\n');
3299     }
3300
3301 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3302   /* check for file conflicts */
3303   if (newpkgs)
3304     {
3305       Queue conflicts;
3306       struct fcstate fcstate;
3307
3308       printf("Searching for file conflicts\n");
3309       queue_init(&conflicts);
3310       fcstate.rpmstate = rpm_state_create(pool, rootdir);
3311       fcstate.newpkgscnt = newpkgs;
3312       fcstate.checkq = &checkq;
3313       fcstate.newpkgsfps = newpkgsfps;
3314       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
3315       fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
3316       if (conflicts.count)
3317         {
3318           printf("\n");
3319           for (i = 0; i < conflicts.count; i += 6)
3320             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]));
3321           printf("\n");
3322           if (yesno("Re-run solver (y/n/q)? "))
3323             {
3324               for (i = 0; i < newpkgs; i++)
3325                 if (newpkgsfps[i])
3326                   fclose(newpkgsfps[i]);
3327               newpkgsfps = solv_free(newpkgsfps);
3328               solver_free(solv);
3329               solv = 0;
3330               pool_add_fileconflicts_deps(pool, &conflicts);
3331               goto rerunsolver;
3332             }
3333         }
3334       queue_free(&conflicts);
3335     }
3336 #endif
3337
3338   /* and finally commit the transaction */
3339   printf("Committing transaction:\n\n");
3340   transaction_order(trans, 0);
3341   for (i = 0; i < trans->steps.count; i++)
3342     {
3343 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3344       const char *evr, *evrp, *nvra;
3345 #endif
3346       Solvable *s;
3347       int j;
3348       FILE *fp;
3349       Id type;
3350
3351       p = trans->steps.elements[i];
3352       s = pool_id2solvable(pool, p);
3353       type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
3354       switch(type)
3355         {
3356         case SOLVER_TRANSACTION_ERASE:
3357           printf("erase %s\n", pool_solvid2str(pool, p));
3358 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3359           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
3360             continue;
3361           /* strip epoch from evr */
3362           evr = evrp = pool_id2str(pool, s->evr);
3363           while (*evrp >= '0' && *evrp <= '9')
3364             evrp++;
3365           if (evrp > evr && evrp[0] == ':' && evrp[1])
3366             evr = evrp + 1;
3367           nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
3368           nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
3369           runrpm("-e", nvra, -1, rootdir);      /* too bad that --querybynumber doesn't work */
3370 #endif
3371 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
3372           rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
3373 #endif
3374           break;
3375         case SOLVER_TRANSACTION_INSTALL:
3376         case SOLVER_TRANSACTION_MULTIINSTALL:
3377           printf("install %s\n", pool_solvid2str(pool, p));
3378           for (j = 0; j < newpkgs; j++)
3379             if (checkq.elements[j] == p)
3380               break;
3381           fp = j < newpkgs ? newpkgsfps[j] : 0;
3382           if (!fp)
3383             continue;
3384           rewind(fp);
3385           lseek(fileno(fp), 0, SEEK_SET);
3386 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3387           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
3388 #endif
3389 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
3390           rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
3391 #endif
3392           fclose(fp);
3393           newpkgsfps[j] = 0;
3394           break;
3395         default:
3396           break;
3397         }
3398     }
3399
3400   for (i = 0; i < newpkgs; i++)
3401     if (newpkgsfps[i])
3402       fclose(newpkgsfps[i]);
3403   solv_free(newpkgsfps);
3404   queue_free(&checkq);
3405   transaction_free(trans);
3406   solver_free(solv);
3407   queue_free(&job);
3408   pool_free(pool);
3409   free_repoinfos(repoinfos, nrepoinfos);
3410   solv_free(commandlinepkgs);
3411 #ifdef FEDORA
3412   yum_substitute(pool, 0);
3413 #endif
3414   exit(0);
3415 }