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