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