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