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