- add experimental support for choice rules to fix #485409
[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 available in the library but missing from solv:
11  * - vendor policy loading
12  * - soft locks file handling
13  * - multi version handling
14  */
15
16 #define _GNU_SOURCE
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <dirent.h>
21 #include <fnmatch.h>
22 #include <unistd.h>
23 #include <zlib.h>
24 #include <fcntl.h>
25 #include <assert.h>
26 #include <sys/utsname.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <time.h>
30 #include <sys/time.h>
31
32 #include <sys/socket.h>
33 #include <netdb.h>
34 #include <poll.h>
35 #include <errno.h>
36
37 #include "pool.h"
38 #include "poolarch.h"
39 #include "repo.h"
40 #include "evr.h"
41 #include "policy.h"
42 #include "util.h"
43 #include "solver.h"
44 #include "solverdebug.h"
45 #include "chksum.h"
46 #include "repo_solv.h"
47
48 #include "repo_write.h"
49 #include "repo_rpmdb.h"
50 #include "repo_products.h"
51 #include "repo_rpmmd.h"
52 #include "repo_susetags.h"
53 #include "repo_repomdxml.h"
54 #include "repo_updateinfoxml.h"
55 #include "repo_deltainfoxml.h"
56 #include "repo_content.h"
57 #include "pool_fileconflicts.h"
58
59
60 #ifdef FEDORA
61 # define REPOINFO_PATH "/etc/yum.repos.d"
62 #else
63 # define REPOINFO_PATH "/etc/zypp/repos.d"
64 # define PRODUCTS_PATH "/etc/products.d"
65 # define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
66 #endif
67
68 #define SOLVCACHE_PATH "/var/cache/solv"
69
70 #define METADATA_EXPIRE (60 * 90)
71
72 struct repoinfo {
73   Repo *repo;
74
75   char *alias;
76   char *name;
77   int enabled;
78   int autorefresh;
79   char *baseurl;
80   char *metalink;
81   char *path;
82   int type;
83   int pkgs_gpgcheck;
84   int repo_gpgcheck;
85   int priority;
86   int keeppackages;
87   int metadata_expire;
88
89   unsigned char cookie[32];
90   unsigned char extcookie[32];
91 };
92
93 #ifdef FEDORA
94 char *
95 yum_substitute(Pool *pool, char *line)
96 {
97   char *p, *p2;
98
99   p = line;
100   while ((p2 = strchr(p, '$')) != 0)
101     {
102       if (!strncmp(p2, "$releasever", 11))
103         {
104           static char *releaseevr;
105           if (!releaseevr)
106             {
107               Queue q;
108         
109               queue_init(&q);
110               rpm_installedrpmdbids(0, "Providename", "redhat-release", &q);
111               if (q.count)
112                 {
113                   void *handle, *state = 0;
114                   char *p;
115                   handle = rpm_byrpmdbid(q.elements[0], 0, &state);
116                   releaseevr = rpm_query(handle, SOLVABLE_EVR);
117                   rpm_byrpmdbid(0, 0, &state);
118                   if ((p = strchr(releaseevr, '-')) != 0)
119                     *p = 0;
120                 }
121               queue_free(&q);
122               if (!releaseevr)
123                 releaseevr = strdup("?");
124             }
125           *p2 = 0;
126           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
127           p2 = p + (p2 - line);
128           line = p;
129           p = p2 + strlen(releaseevr);
130           continue;
131         }
132       if (!strncmp(p2, "$basearch", 9))
133         {
134           static char *basearch;
135           if (!basearch)
136             {
137               struct utsname un;
138               if (uname(&un))
139                 {
140                   perror("uname");
141                   exit(1);
142                 }
143               basearch = strdup(un.machine);
144               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
145                 basearch[1] = '3';
146             }
147           *p2 = 0;
148           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
149           p2 = p + (p2 - line);
150           line = p;
151           p = p2 + strlen(basearch);
152           continue;
153         }
154       p = p2 + 1;
155     }
156   return line;
157 }
158 #endif
159
160 #define TYPE_UNKNOWN    0
161 #define TYPE_SUSETAGS   1
162 #define TYPE_RPMMD      2
163 #define TYPE_PLAINDIR   3
164
165 static int
166 read_repoinfos_sort(const void *ap, const void *bp)
167 {
168   const struct repoinfo *a = ap;
169   const struct repoinfo *b = bp;
170   return strcmp(a->alias, b->alias);
171 }
172
173 struct repoinfo *
174 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
175 {
176   char buf[4096];
177   char buf2[4096], *kp, *vp, *kpe;
178   DIR *dir;
179   FILE *fp;
180   struct dirent *ent;
181   int l, rdlen;
182   struct repoinfo *repoinfos = 0, *cinfo;
183   int nrepoinfos = 0;
184
185   rdlen = strlen(reposdir);
186   dir = opendir(reposdir);
187   if (!dir)
188     {
189       *nrepoinfosp = 0;
190       return 0;
191     }
192   while ((ent = readdir(dir)) != 0)
193     {
194       l = strlen(ent->d_name);
195       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
196         continue;
197       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
198       if ((fp = fopen(buf, "r")) == 0)
199         {
200           perror(buf);
201           continue;
202         }
203       cinfo = 0;
204       while(fgets(buf2, sizeof(buf2), fp))
205         {
206           l = strlen(buf2);
207           if (l == 0)
208             continue;
209           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
210             buf2[--l] = 0;
211           kp = buf2;
212           while (*kp == ' ' || *kp == '\t')
213             kp++;
214           if (!*kp || *kp == '#')
215             continue;
216 #ifdef FEDORA
217           if (strchr(kp, '$'))
218             kp = yum_substitute(pool, kp);
219 #endif
220           if (*kp == '[')
221             {
222               vp = strrchr(kp, ']');
223               if (!vp)
224                 continue;
225               *vp = 0;
226               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
227               cinfo = repoinfos + nrepoinfos++;
228               memset(cinfo, 0, sizeof(*cinfo));
229               cinfo->alias = strdup(kp + 1);
230               cinfo->type = TYPE_RPMMD;
231               cinfo->autorefresh = 1;
232 #ifndef FEDORA
233               cinfo->repo_gpgcheck = 1;
234 #endif
235               cinfo->metadata_expire = METADATA_EXPIRE;
236               continue;
237             }
238           if (!cinfo)
239             continue;
240           vp = strchr(kp, '=');
241           if (!vp)
242             continue;
243           for (kpe = vp - 1; kpe >= kp; kpe--)
244             if (*kpe != ' ' && *kpe != '\t')
245               break;
246           if (kpe == kp)
247             continue;
248           vp++;
249           while (*vp == ' ' || *vp == '\t')
250             vp++;
251           kpe[1] = 0;
252           if (!strcmp(kp, "name"))
253             cinfo->name = strdup(vp);
254           else if (!strcmp(kp, "enabled"))
255             cinfo->enabled = *vp == '0' ? 0 : 1;
256           else if (!strcmp(kp, "autorefresh"))
257             cinfo->autorefresh = *vp == '0' ? 0 : 1;
258           else if (!strcmp(kp, "gpgcheck"))
259             cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
260           else if (!strcmp(kp, "repo_gpgcheck"))
261             cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
262           else if (!strcmp(kp, "baseurl"))
263             cinfo->baseurl = strdup(vp);
264           else if (!strcmp(kp, "mirrorlist"))
265             {
266               if (strstr(vp, "metalink"))
267                 cinfo->metalink = strdup(vp);
268             }
269           else if (!strcmp(kp, "path"))
270             {
271               if (vp && strcmp(vp, "/") != 0)
272                 cinfo->path = strdup(vp);
273             }
274           else if (!strcmp(kp, "type"))
275             {
276               if (!strcmp(vp, "yast2"))
277                 cinfo->type = TYPE_SUSETAGS;
278               else if (!strcmp(vp, "rpm-md"))
279                 cinfo->type = TYPE_RPMMD;
280               else if (!strcmp(vp, "plaindir"))
281                 cinfo->type = TYPE_PLAINDIR;
282               else
283                 cinfo->type = TYPE_UNKNOWN;
284             }
285           else if (!strcmp(kp, "priority"))
286             cinfo->priority = atoi(vp);
287           else if (!strcmp(kp, "keeppackages"))
288             cinfo->keeppackages = *vp == '0' ? 0 : 1;
289         }
290       fclose(fp);
291       cinfo = 0;
292     }
293   closedir(dir);
294   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
295   *nrepoinfosp = nrepoinfos;
296   return repoinfos;
297 }
298
299 void
300 free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
301 {
302   int i;
303   for (i = 0; i < nrepoinfos; i++)
304     {
305       struct repoinfo *cinfo = repoinfos + i;
306       sat_free(cinfo->name);
307       sat_free(cinfo->alias);
308       sat_free(cinfo->path);
309       sat_free(cinfo->metalink);
310       sat_free(cinfo->baseurl);
311     }
312   sat_free(repoinfos);
313 }
314
315 static inline int
316 opentmpfile()
317 {
318   char tmpl[100];
319   int fd;
320
321   strcpy(tmpl, "/var/tmp/solvXXXXXX");
322   fd = mkstemp(tmpl);
323   if (fd < 0)
324     {
325       perror("mkstemp");
326       exit(1);
327     }
328   unlink(tmpl);
329   return fd;
330 }
331
332 static int
333 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
334 {
335   char buf[1024];
336   unsigned char *sum;
337   void *h;
338   int l;
339
340   h = sat_chksum_create(chksumtype);
341   if (!h)
342     {
343       printf("%s: unknown checksum type\n", file);
344       return 0;
345     }
346   while ((l = read(fd, buf, sizeof(buf))) > 0)
347     sat_chksum_add(h, buf, l);
348   lseek(fd, 0, SEEK_SET);
349   l = 0;
350   sum = sat_chksum_get(h, &l);
351   if (memcmp(sum, chksum, l))
352     {
353       printf("%s: checksum mismatch\n", file);
354       return 0;
355     }
356   return 1;
357 }
358
359 void
360 findfastest(char **urls, int nurls)
361 {
362   int i, j, port;
363   int *socks, qc;
364   struct pollfd *fds;
365   char *p, *p2, *q;
366   char portstr[16];
367   struct addrinfo hints, *result;;
368
369   fds = sat_calloc(nurls, sizeof(*fds));
370   socks = sat_calloc(nurls, sizeof(*socks));
371   for (i = 0; i < nurls; i++)
372     {
373       socks[i] = -1;
374       p = strchr(urls[i], '/');
375       if (!p)
376         continue;
377       if (p[1] != '/')
378         continue;
379       p += 2;
380       q = strchr(p, '/');
381       qc = 0;
382       if (q)
383         {
384           qc = *q;
385           *q = 0;
386         }
387       if ((p2 = strchr(p, '@')) != 0)
388         p = p2 + 1;
389       port = 80;
390       if (!strncmp("https:", urls[i], 6))
391         port = 443;
392       else if (!strncmp("ftp:", urls[i], 4))
393         port = 21;
394       if ((p2 = strrchr(p, ':')) != 0)
395         {
396           port = atoi(p2 + 1);
397           if (q)
398             *q = qc;
399           q = p2;
400           qc = *q;
401           *q = 0;
402         }
403       sprintf(portstr, "%d", port);
404       memset(&hints, 0, sizeof(struct addrinfo));
405       hints.ai_family = AF_UNSPEC;
406       hints.ai_socktype = SOCK_STREAM;
407       hints.ai_flags = AI_NUMERICSERV;
408       result = 0;
409       if (!getaddrinfo(p, portstr, &hints, &result))
410         {
411           socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
412           if (socks[i] >= 0)
413             {
414               fcntl(socks[i], F_SETFL, O_NONBLOCK);
415               if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
416                 {
417                   if (errno != EINPROGRESS)
418                     {
419                       close(socks[i]);
420                       socks[i] = -1;
421                     }
422                 }
423             }
424           freeaddrinfo(result);
425         }
426       if (q)
427         *q = qc;
428     }
429   for (;;)
430     {
431       for (i = j = 0; i < nurls; i++)
432         {
433           if (socks[i] < 0)
434             continue;
435           fds[j].fd = socks[i];
436           fds[j].events = POLLOUT;
437           j++;
438         }
439       if (j < 2)
440         {
441           i = j - 1;
442           break;
443         }
444       if (poll(fds, j, 10000) <= 0)
445         {
446           i = -1;       /* something is wrong */
447           break;
448         }
449       for (i = 0; i < j; i++)
450         if ((fds[i].revents & POLLOUT) != 0)
451           {
452             int soe = 0;
453             socklen_t soel = sizeof(int);
454             if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
455               {
456                 /* connect failed, kill socket */
457                 for (j = 0; j < nurls; j++)
458                   if (socks[j] == fds[i].fd)
459                     {
460                       close(socks[j]);
461                       socks[j] = -1;
462                     }
463                 i = j + 1;
464                 break;
465               }
466             break;      /* horray! */
467           }
468       if (i == j + 1)
469         continue;
470       if (i == j)
471         i = -1;         /* something is wrong, no bit was set */
472       break;
473     }
474   /* now i contains the fastest fd index */
475   if (i >= 0)
476     {
477       for (j = 0; j < nurls; j++)
478         if (socks[j] == fds[i].fd)
479           break;
480       if (j != 0)
481         {
482           char *url0 = urls[0];
483           urls[0] = urls[j];
484           urls[j] = url0;
485         }
486     }
487   for (i = j = 0; i < nurls; i++)
488     if (socks[i] >= 0)
489       close(socks[i]);
490   free(socks);
491   free(fds);
492 }
493
494 char *
495 findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
496 {
497   char buf[4096], *bp, *ep;
498   char **urls = 0;
499   int nurls = 0;
500   int i;
501
502   if (chksumtypep)
503     *chksumtypep = 0;
504   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
505     {
506       while (*bp == ' ' || *bp == '\t')
507         bp++;
508       if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
509         {
510           int i;
511
512           bp += 20;
513           memset(chksump, 0, 32);
514           for (i = 0; i < 64; i++)
515             {
516               int c = *bp++;
517               if (c >= '0' && c <= '9')
518                 chksump[i / 2] = chksump[i / 2] * 16 + (c - '0');
519               else if (c >= 'a' && c <= 'f')
520                 chksump[i / 2] = chksump[i / 2] * 16 + (c - ('a' - 10));
521               else if (c >= 'A' && c <= 'F')
522                 chksump[i / 2] = chksump[i / 2] * 16 + (c - ('A' - 10));
523               else
524                 break;
525             }
526           if (i == 64)
527             *chksumtypep = REPOKEY_TYPE_SHA256;
528           continue;
529         }
530       if (strncmp(bp, "<url", 4))
531         continue;
532       bp = strchr(bp, '>');
533       if (!bp)
534         continue;
535       bp++;
536       ep = strstr(bp, "repodata/repomd.xml</url>");
537       if (!ep)
538         continue;
539       *ep = 0;
540       if (strncmp(bp, "http", 4))
541         continue;
542       urls = sat_extend(urls, nurls, 1, sizeof(*urls), 15);
543       urls[nurls++] = strdup(bp);
544     }
545   if (nurls)
546     {
547       if (nurls > 1)
548         findfastest(urls, nurls > 5 ? 5 : nurls);
549       bp = urls[0];
550       urls[0] = 0;
551       for (i = 0; i < nurls; i++)
552         sat_free(urls[i]);
553       sat_free(urls);
554       ep = strchr(bp, '/');
555       if ((ep = strchr(ep + 2, '/')) != 0)
556         {
557           *ep = 0;
558           printf("[using mirror %s]\n", bp);
559           *ep = '/';
560         }
561       return bp;
562     }
563   return 0;
564 }
565
566 static ssize_t
567 cookie_gzread(void *cookie, char *buf, size_t nbytes)
568 {
569   return gzread((gzFile *)cookie, buf, nbytes);
570 }
571
572 static int
573 cookie_gzclose(void *cookie)
574 {
575   return gzclose((gzFile *)cookie);
576 }
577
578 FILE *
579 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int *badchecksump)
580 {
581   pid_t pid;
582   int fd, l;
583   int status;
584   char url[4096];
585   const char *baseurl = cinfo->baseurl;
586
587   if (!baseurl)
588     {
589       if (!cinfo->metalink)
590         return 0;
591       if (file != cinfo->metalink)
592         {
593           FILE *fp = curlfopen(cinfo, cinfo->metalink, 0, 0, 0, 0);
594           unsigned char mlchksum[32];
595           Id mlchksumtype = 0;
596           if (!fp)
597             return 0;
598           cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
599           fclose(fp);
600           if (!cinfo->baseurl)
601             return 0;
602           if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
603             {
604               chksumtype = mlchksumtype;
605               chksum = mlchksum;
606             }
607           return curlfopen(cinfo, file, uncompress, chksum, chksumtype, badchecksump);
608         }
609       snprintf(url, sizeof(url), "%s", file);
610     }
611   else
612     {
613       l = strlen(baseurl);
614       if (l && baseurl[l - 1] == '/')
615         snprintf(url, sizeof(url), "%s%s", baseurl, file);
616       else
617         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
618     }
619   fd = opentmpfile();
620   // printf("url: %s\n", url);
621   if ((pid = fork()) == (pid_t)-1)
622     {
623       perror("fork");
624       exit(1);
625     }
626   if (pid == 0)
627     {
628       if (fd != 1)
629         {
630           dup2(fd, 1);
631           close(fd);
632         }
633       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
634       perror("curl");
635       _exit(0);
636     }
637   status = 0;
638   while (waitpid(pid, &status, 0) != pid)
639     ;
640   if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
641     {
642       /* empty file */
643       close(fd);
644       return 0;
645     }
646   lseek(fd, 0, SEEK_SET);
647   if (status)
648     {
649       printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
650       if (badchecksump)
651         *badchecksump = 1;
652       close(fd);
653       return 0;
654     }
655   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
656     {
657       if (badchecksump)
658         *badchecksump = 1;
659       close(fd);
660       return 0;
661     }
662   if (uncompress)
663     {
664       char tmpl[100];
665       cookie_io_functions_t cio;
666       gzFile *gzf;
667
668       sprintf(tmpl, "/dev/fd/%d", fd);
669       gzf = gzopen(tmpl, "r");
670       close(fd);
671       if (!gzf)
672         {
673           fprintf(stderr, "could not open /dev/fd/%d, /proc not mounted?\n", fd);
674           exit(1);
675         }
676       memset(&cio, 0, sizeof(cio));
677       cio.read = cookie_gzread;
678       cio.close = cookie_gzclose;
679       return fopencookie(gzf, "r", cio);
680     }
681   fcntl(fd, F_SETFD, FD_CLOEXEC);
682   return fdopen(fd, "r");
683 }
684
685 static void
686 cleanupgpg(char *gpgdir)
687 {
688   char cmd[256];
689   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
690   unlink(cmd);
691   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
692   unlink(cmd);
693   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
694   unlink(cmd);
695   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
696   unlink(cmd);
697   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
698   unlink(cmd);
699   rmdir(gpgdir);
700 }
701
702 int
703 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
704 {
705   char *gpgdir;
706   char *keysfile;
707   const char *pubkey;
708   char cmd[256];
709   FILE *kfp;
710   Solvable *s;
711   Id p;
712   off_t posfp, possigfp;
713   int r, nkeys;
714
715   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
716   if (!gpgdir)
717     return 0;
718   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
719   if (!(kfp = fopen(keysfile, "w")) )
720     {
721       cleanupgpg(gpgdir);
722       return 0;
723     }
724   nkeys = 0;
725   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
726     {
727       if (!s->repo)
728         continue;
729       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
730       if (!pubkey || !*pubkey)
731         continue;
732       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
733         break;
734       if (fputc('\n', kfp) == EOF)      /* Just in case... */
735         break;
736       nkeys++;
737     }
738   if (fclose(kfp) || !nkeys)
739     {
740       cleanupgpg(gpgdir);
741       return 0;
742     }
743   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
744   if (system(cmd))
745     {
746       fprintf(stderr, "key import error\n");
747       cleanupgpg(gpgdir);
748       return 0;
749     }
750   unlink(keysfile);
751   posfp = lseek(fileno(fp), 0, SEEK_CUR);
752   lseek(fileno(fp), 0, SEEK_SET);
753   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
754   lseek(fileno(sigfp), 0, SEEK_SET);
755   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
756   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
757   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
758   r = system(cmd);
759   lseek(fileno(sigfp), possigfp, SEEK_SET);
760   lseek(fileno(fp), posfp, SEEK_SET);
761   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
762   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
763   cleanupgpg(gpgdir);
764   return r == 0 ? 1 : 0;
765 }
766
767 #define CHKSUM_IDENT "1.1"
768
769 void
770 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
771 {
772   char buf[4096];
773   void *h = sat_chksum_create(chktype);
774   int l;
775
776   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
777   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
778     sat_chksum_add(h, buf, l);
779   rewind(fp);
780   sat_chksum_free(h, out);
781 }
782
783 void
784 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
785 {
786   void *h = sat_chksum_create(chktype);
787   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
788   sat_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
789   sat_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
790   sat_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
791   sat_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
792   sat_chksum_free(h, out);
793 }
794
795 void
796 setarch(Pool *pool)
797 {
798   struct utsname un;
799   if (uname(&un))
800     {
801       perror("uname");
802       exit(1);
803     }
804   pool_setarch(pool, un.machine);
805 }
806
807 char *calccachepath(Repo *repo, const char *repoext)
808 {
809   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
810   if (repoext)
811     {
812       p = pool_tmpjoin(repo->pool, p, "_", repoext);
813       p = pool_tmpjoin(repo->pool, p, ".solvx", 0);
814     }
815   else
816     p = pool_tmpjoin(repo->pool, p, ".solv", 0);
817   q = p + strlen(SOLVCACHE_PATH) + 1;
818   if (*q == '.')
819     *q = '_';
820   for (; *q; q++)
821     if (*q == '/')
822       *q = '_';
823   return p;
824 }
825
826 int
827 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
828 {
829   FILE *fp;
830   unsigned char mycookie[32];
831   struct repoinfo *cinfo;
832   int flags;
833
834   cinfo = repo->appdata;
835   if (!(fp = fopen(calccachepath(repo, repoext), "r")))
836     return 0;
837   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
838     {
839       fclose(fp);
840       return 0;
841     }
842   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
843     {
844       fclose(fp);
845       return 0;
846     }
847   rewind(fp);
848
849   flags = 0;
850   if (repoext)
851     flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
852   if (repoext && strcmp(repoext, "DL") != 0)
853     flags |= REPO_LOCALPOOL;    /* no local pool for DL so that we can compare IDs */
854
855   if (repo_add_solv_flags(repo, fp, flags))
856     {
857       fclose(fp);
858       return 0;
859     }
860   if (cinfo && !repoext)
861     {
862       /* set the checksum so that we can use it with the stub loads */
863       struct stat stb;
864       if (!fstat(fileno(fp), &stb))
865         {
866           int i;
867
868           stb.st_mtime = 0;
869           calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
870           for (i = 0; i < 32; i++)
871             cinfo->extcookie[i] ^= cinfo->cookie[i];
872         }
873     }
874   if (mark)
875     futimes(fileno(fp), 0);     /* try to set modification time */
876   fclose(fp);
877   return 1;
878 }
879
880 void
881 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
882 {
883   Id *addedfileprovides = 0;
884   FILE *fp;
885   int i, fd;
886   char *tmpl;
887   int myinfo = 0;
888   struct repoinfo *cinfo;
889   int onepiece;
890
891   cinfo = repo->appdata;
892   mkdir(SOLVCACHE_PATH, 0755);
893   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
894   fd = mkstemp(tmpl);
895   if (fd < 0)
896     {
897       free(tmpl);
898       return;
899     }
900   fchmod(fd, 0444);
901   if (!(fp = fdopen(fd, "w")))
902     {
903       close(fd);
904       unlink(tmpl);
905       free(tmpl);
906       return;
907     }
908
909   onepiece = 1;
910   for (i = repo->start; i < repo->end; i++)
911    if (repo->pool->solvables[i].repo != repo)
912      break;
913   if (i < repo->end)
914     onepiece = 0;
915
916   if (!repoext)
917     {
918       if (!info)
919         {
920           info = repo_add_repodata(repo, 0);
921           myinfo = 1;
922         }
923       pool_addfileprovides_ids(repo->pool, 0, &addedfileprovides);
924       if (addedfileprovides && *addedfileprovides)
925         {
926           for (i = 0; addedfileprovides[i]; i++)
927             repodata_add_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[i]);
928         }
929       sat_free(addedfileprovides);
930       repodata_internalize(info);
931       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
932     }
933   else
934     repodata_write(info, fp, repo_write_stdkeyfilter, 0);
935   if (myinfo)
936     repodata_free(info);
937   if (fwrite(cookie, 32, 1, fp) != 1)
938     {
939       fclose(fp);
940       unlink(tmpl);
941       free(tmpl);
942       return;
943     }
944   if (fclose(fp))
945     {
946       unlink(tmpl);
947       free(tmpl);
948       return;
949     }
950   if (!repoext && cinfo)
951     {
952       /* set the checksum so that we can use it with the stub loads */
953       struct stat stb;
954       if (!stat(tmpl, &stb))
955         {
956           int i;
957
958           stb.st_mtime = 0;
959           calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
960           for (i = 0; i < 32; i++)
961             cinfo->extcookie[i] ^= cinfo->cookie[i];
962         }
963     }
964   if (onepiece)
965     {
966       /* switch to just saved repo to activate paging and save memory */
967       FILE *fp = fopen(tmpl, "r");
968       if (fp)
969         {
970           if (!repoext)
971             {
972               /* main repo */
973               repo_empty(repo, 1);
974               if (repo_add_solv_flags(repo, fp, SOLV_ADD_NO_STUBS))
975                 {
976                   /* oops, no way to recover from here */
977                   fprintf(stderr, "internal error\n");
978                   exit(1);
979                 }
980             }
981           else
982             {
983               /* make sure repodata contains complete repo */
984               /* (this is how repodata_write saves it) */
985               repodata_extend_block(info, repo->start, repo->end - repo->start);
986               info->state = REPODATA_LOADING;
987               /* no need for LOCALPOOL as pool already contains ids */
988               repo_add_solv_flags(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
989               info->state = REPODATA_AVAILABLE; /* in case the load failed */
990             }
991           fclose(fp);
992         }
993     }
994   if (!rename(tmpl, calccachepath(repo, repoext)))
995     unlink(tmpl);
996   free(tmpl);
997 }
998
999
1000 static Pool *
1001 read_sigs()
1002 {
1003   Pool *sigpool = pool_create();
1004   Repo *repo = repo_create(sigpool, "rpmdbkeys");
1005   repo_add_rpmdb_pubkeys(repo, 0, 0);
1006   return sigpool;
1007 }
1008
1009
1010 /* repomd helpers */
1011
1012 static inline const char *
1013 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1014 {
1015   Pool *pool = repo->pool;
1016   Dataiterator di;
1017   const char *filename;
1018
1019   filename = 0;
1020   *chksump = 0;
1021   *chksumtypep = 0;
1022   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1023   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1024   if (dataiterator_step(&di))
1025     {
1026       dataiterator_setpos_parent(&di);
1027       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1028       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
1029     }
1030   dataiterator_free(&di);
1031   if (filename && !*chksumtypep)
1032     {
1033       printf("no %s file checksum!\n", what);
1034       filename = 0;
1035     }
1036   return filename;
1037 }
1038
1039 int
1040 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
1041 {
1042   Pool *pool = repo->pool;
1043   Dataiterator di;
1044   Id chksumtype, handle;
1045   const unsigned char *chksum;
1046   const char *filename;
1047
1048   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1049   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1050   if (!dataiterator_step(&di))
1051     {
1052       dataiterator_free(&di);
1053       return 0;
1054     }
1055   if (!strcmp(what, "prestodelta"))
1056     what = "deltainfo";
1057   dataiterator_setpos_parent(&di);
1058   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1059   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
1060   if (!filename || !chksum)
1061     {
1062       dataiterator_free(&di);
1063       return 0;
1064     }
1065   handle = repodata_new_handle(data);
1066   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1067   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1068   if (chksumtype)
1069     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1070   if (!strcmp(what, "deltainfo"))
1071     {
1072       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1073       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1074     }
1075   if (!strcmp(what, "filelists"))
1076     {
1077       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1078       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1079     }
1080   dataiterator_free(&di);
1081   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1082   return 1;
1083 }
1084
1085
1086 /* susetags helpers */
1087
1088 static inline const char *
1089 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1090 {
1091   Pool *pool = repo->pool;
1092   Dataiterator di;
1093   const char *filename;
1094
1095   filename = 0;
1096   *chksump = 0;
1097   *chksumtypep = 0;
1098   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1099   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1100   if (dataiterator_step(&di))
1101     {
1102       dataiterator_setpos_parent(&di);
1103       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1104       filename = what;
1105     }
1106   dataiterator_free(&di);
1107   if (filename && !*chksumtypep)
1108     {
1109       printf("no %s file checksum!\n", what);
1110       filename = 0;
1111     }
1112   return filename;
1113 }
1114
1115 static Id susetags_langtags[] = {
1116   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1117   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1118   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1119   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1120   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1121   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1122   0, 0
1123 };
1124
1125 void
1126 susetags_add_ext(Repo *repo, Repodata *data)
1127 {
1128   Pool *pool = repo->pool;
1129   Dataiterator di;
1130   char ext[3];
1131   Id handle, filechksumtype;
1132   const unsigned char *filechksum;
1133   int i;
1134
1135   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1136   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1137   while (dataiterator_step(&di))
1138     {
1139       if (strncmp(di.kv.str, "packages.", 9) != 0)
1140         continue;
1141       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1142         continue;
1143       ext[0] = di.kv.str[9];
1144       ext[1] = di.kv.str[10];
1145       ext[2] = 0;
1146       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1147         continue;
1148       handle = repodata_new_handle(data);
1149       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1150       if (filechksumtype)
1151         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1152       if (!strcmp(ext, "DU"))
1153         {
1154           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1155           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1156         }
1157       if (!strcmp(ext, "FL"))
1158         {
1159           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1160           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1161         }
1162       else
1163         {
1164           for (i = 0; susetags_langtags[i]; i += 2)
1165             {
1166               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1167               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1168             }
1169         }
1170       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1171     }
1172   dataiterator_free(&di);
1173 }
1174
1175
1176 static inline int
1177 iscompressed(const char *name)
1178 {
1179   int l = strlen(name);
1180   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
1181 }
1182
1183
1184 /* load callback */
1185
1186 int
1187 load_stub(Pool *pool, Repodata *data, void *dp)
1188 {
1189   const char *filename, *descrdir, *repomdtype;
1190   const unsigned char *filechksum;
1191   Id filechksumtype;
1192   struct repoinfo *cinfo;
1193   FILE *fp;
1194   Id defvendor;
1195   char ext[3];
1196
1197   cinfo = data->repo->appdata;
1198
1199   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1200   if (filename)
1201     {
1202       /* susetags load */
1203       ext[0] = filename[9];
1204       ext[1] = filename[10];
1205       ext[2] = 0;
1206 #if 1
1207       printf("[%s:%s", data->repo->name, ext);
1208 #endif
1209       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1210         {
1211           printf(" cached]\n"); fflush(stdout);
1212           return 1;
1213         }
1214 #if 1
1215       printf(" loading]\n"); fflush(stdout);
1216 #endif
1217       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1218       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
1219       if (!descrdir)
1220         descrdir = "suse/setup/descr";
1221       filechksumtype = 0;
1222       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1223       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1224         return 0;
1225       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1226       fclose(fp);
1227       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1228       return 1;
1229     }
1230
1231   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1232   if (repomdtype)
1233     {
1234       if (!strcmp(repomdtype, "filelists"))
1235         strcpy(ext, "FL");
1236       else if (!strcmp(repomdtype, "deltainfo"))
1237         strcpy(ext, "DL");
1238       else
1239         return 0;
1240 #if 1
1241       printf("[%s:%s", data->repo->name, ext);
1242 #endif
1243       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1244         {
1245           printf(" cached]\n");fflush(stdout);
1246           return 1;
1247         }
1248       printf(" loading]\n"); fflush(stdout);
1249       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1250       filechksumtype = 0;
1251       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1252       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1253         return 0;
1254       if (!strcmp(ext, "FL"))
1255         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1256       else if (!strcmp(ext, "DL"))
1257         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1258       fclose(fp);
1259       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1260       return 1;
1261     }
1262
1263   return 0;
1264 }
1265
1266 void
1267 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1268 {
1269   Repo *repo;
1270   struct repoinfo *cinfo;
1271   int i;
1272   FILE *fp;
1273   FILE *sigfp;
1274   const char *filename;
1275   const unsigned char *filechksum;
1276   Id filechksumtype;
1277   const char *descrdir;
1278   int defvendor;
1279   struct stat stb;
1280   unsigned char cookie[32];
1281   Pool *sigpool = 0;
1282   Repodata *data;
1283   int badchecksum;
1284   int dorefresh;
1285
1286   repo = repo_create(pool, "@System");
1287   printf("rpm database:");
1288   if (stat("/var/lib/rpm/Packages", &stb))
1289     memset(&stb, 0, sizeof(&stb));
1290   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie);
1291   if (usecachedrepo(repo, 0, cookie, 0))
1292     printf(" cached\n");
1293   else
1294     {
1295       FILE *ofp;
1296       printf(" reading\n");
1297       int done = 0;
1298
1299 #ifdef PRODUCTS_PATH
1300       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1301 #endif
1302       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1303         {
1304           Repo *ref = repo_create(pool, "@System.old");
1305           if (!repo_add_solv(ref, ofp))
1306             {
1307               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1308               done = 1;
1309             }
1310           fclose(ofp);
1311           repo_free(ref, 1);
1312         }
1313       if (!done)
1314         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1315       writecachedrepo(repo, 0, 0, cookie);
1316     }
1317   pool_set_installed(pool, repo);
1318
1319   for (i = 0; i < nrepoinfos; i++)
1320     {
1321       cinfo = repoinfos + i;
1322       if (!cinfo->enabled)
1323         continue;
1324
1325       repo = repo_create(pool, cinfo->alias);
1326       cinfo->repo = repo;
1327       repo->appdata = cinfo;
1328       repo->priority = 99 - cinfo->priority;
1329
1330       dorefresh = cinfo->autorefresh;
1331       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1332         {
1333           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1334             dorefresh = 0;
1335         }
1336       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1337         {
1338           printf("repo '%s':", cinfo->alias);
1339           printf(" cached\n");
1340           continue;
1341         }
1342       badchecksum = 0;
1343       switch (cinfo->type)
1344         {
1345         case TYPE_RPMMD:
1346           printf("rpmmd repo '%s':", cinfo->alias);
1347           fflush(stdout);
1348           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1349             {
1350               printf(" no repomd.xml file, skipped\n");
1351               repo_free(repo, 1);
1352               cinfo->repo = 0;
1353               break;
1354             }
1355           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1356           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1357             {
1358               printf(" cached\n");
1359               fclose(fp);
1360               break;
1361             }
1362           if (cinfo->repo_gpgcheck)
1363             {
1364               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1365               if (!sigfp)
1366                 {
1367                   printf(" unsigned, skipped\n");
1368                   fclose(fp);
1369                   break;
1370                 }
1371               if (!sigpool)
1372                 sigpool = read_sigs();
1373               if (!checksig(sigpool, fp, sigfp))
1374                 {
1375                   printf(" checksig failed, skipped\n");
1376                   fclose(sigfp);
1377                   fclose(fp);
1378                   break;
1379                 }
1380               fclose(sigfp);
1381             }
1382           repo_add_repomdxml(repo, fp, 0);
1383           fclose(fp);
1384           printf(" reading\n");
1385           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1386           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1387             {
1388               repo_add_rpmmd(repo, fp, 0, 0);
1389               fclose(fp);
1390             }
1391           if (badchecksum)
1392             break;      /* hopeless */
1393
1394           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1395           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1396             {
1397               repo_add_updateinfoxml(repo, fp, 0);
1398               fclose(fp);
1399             }
1400
1401           data = repo_add_repodata(repo, 0);
1402           if (!repomd_add_ext(repo, data, "deltainfo"))
1403             repomd_add_ext(repo, data, "prestodelta");
1404           repomd_add_ext(repo, data, "filelists");
1405           repodata_internalize(data);
1406           if (!badchecksum)
1407             writecachedrepo(repo, data, 0, cinfo->cookie);
1408           repodata_create_stubs(repo_last_repodata(repo));
1409           break;
1410
1411         case TYPE_SUSETAGS:
1412           printf("susetags repo '%s':", cinfo->alias);
1413           fflush(stdout);
1414           descrdir = 0;
1415           defvendor = 0;
1416           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1417             {
1418               printf(" no content file, skipped\n");
1419               repo_free(repo, 1);
1420               cinfo->repo = 0;
1421               break;
1422             }
1423           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1424           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1425             {
1426               printf(" cached\n");
1427               fclose(fp);
1428               break;
1429             }
1430           if (cinfo->repo_gpgcheck)
1431             {
1432               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1433               if (!sigfp)
1434                 {
1435                   printf(" unsigned, skipped\n");
1436                   fclose(fp);
1437                   break;
1438                 }
1439               if (sigfp)
1440                 {
1441                   if (!sigpool)
1442                     sigpool = read_sigs();
1443                   if (!checksig(sigpool, fp, sigfp))
1444                     {
1445                       printf(" checksig failed, skipped\n");
1446                       fclose(sigfp);
1447                       fclose(fp);
1448                       break;
1449                     }
1450                   fclose(sigfp);
1451                 }
1452             }
1453           repo_add_content(repo, fp, 0);
1454           fclose(fp);
1455           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1456           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1457           if (!descrdir)
1458             descrdir = "suse/setup/descr";
1459           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1460           if (!filename)
1461             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1462           if (!filename)
1463             {
1464               printf(" no packages file entry, skipped\n");
1465               break;
1466             }
1467           printf(" reading\n");
1468           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1469             break;      /* hopeless */
1470           repo_add_susetags(repo, fp, defvendor, 0, 0);
1471           fclose(fp);
1472           data = repo_add_repodata(repo, 0);
1473           susetags_add_ext(repo, data);
1474           repodata_internalize(data);
1475           if (!badchecksum)
1476             writecachedrepo(repo, data, 0, cinfo->cookie);
1477           repodata_create_stubs(repo_last_repodata(repo));
1478           break;
1479         default:
1480           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1481           repo_free(repo, 1);
1482           cinfo->repo = 0;
1483           break;
1484         }
1485     }
1486   if (sigpool)
1487     pool_free(sigpool);
1488 }
1489
1490
1491 int
1492 str2archid(Pool *pool, char *arch)
1493 {
1494   Id id;
1495   if (!*arch)
1496     return 0;
1497   id = str2id(pool, arch, 0);
1498   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1499     return id;
1500   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1501     return 0;
1502   return id;
1503 }
1504
1505 int
1506 depglob(Pool *pool, char *name, Queue *job)
1507 {
1508   Id p, pp;
1509   Id id = str2id(pool, name, 0);
1510   int i, match = 0;
1511
1512   if (id)
1513     {
1514       FOR_PROVIDES(p, pp, id)
1515         {
1516           Solvable *s = pool->solvables + p;
1517           match = 1;
1518           if (s->name == id)
1519             {
1520               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1521               return 1;
1522             }
1523         }
1524       if (match)
1525         {
1526           printf("[using capability match for '%s']\n", name);
1527           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1528           return 1;
1529         }
1530     }
1531
1532   if (strpbrk(name, "[*?") == 0)
1533     return 0;
1534
1535   /* looks like a name glob. hard work. */
1536   for (p = 1; p < pool->nsolvables; p++)
1537     {
1538       Solvable *s = pool->solvables + p;
1539       if (!s->repo || !pool_installable(pool, s))
1540         continue;
1541       id = s->name;
1542       if (fnmatch(name, id2str(pool, id), 0) == 0)
1543         {
1544           for (i = 0; i < job->count; i += 2)
1545             if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
1546               break;
1547           if (i == job->count)
1548             queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1549           match = 1;
1550         }
1551     }
1552   if (match)
1553     return 1;
1554   /* looks like a dep glob. really hard work. */
1555   for (id = 1; id < pool->ss.nstrings; id++)
1556     {
1557       if (!pool->whatprovides[id])
1558         continue;
1559       if (fnmatch(name, id2str(pool, id), 0) == 0)
1560         {
1561           if (!match)
1562             printf("[using capability match for '%s']\n", name);
1563           for (i = 0; i < job->count; i += 2)
1564             if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
1565               break;
1566           if (i == job->count)
1567             queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1568           match = 1;
1569         }
1570     }
1571   if (match)
1572     return 1;
1573   return 0;
1574 }
1575
1576 void
1577 addrelation(Pool *pool, Queue *job, int flags, Id evr)
1578 {
1579   int i;
1580   for (i = 0; i < job->count; i += 2)
1581     {
1582       if (job->elements[i] != SOLVER_SOLVABLE_NAME && job->elements[i] != SOLVER_SOLVABLE_PROVIDES)
1583         continue;
1584       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
1585     }
1586 }
1587
1588 int
1589 limitevr(Pool *pool, char *evr, Queue *job, Id archid)
1590 {
1591   Queue mq;
1592   Id p, pp, evrid;
1593   int matched = 0;
1594   int i, j;
1595   Solvable *s;
1596
1597   queue_init(&mq);
1598   for (i = 0; i < job->count; i += 2)
1599     {
1600       queue_empty(&mq);
1601       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
1602         {
1603           s = pool_id2solvable(pool, p);
1604           if (archid && s->arch != archid)
1605             continue;
1606           if (evrcmp_str(pool, id2str(pool, s->evr), evr, EVRCMP_MATCH) == 0)
1607              queue_push(&mq, p);
1608         }
1609       if (mq.count)
1610         {
1611           if (!matched && i)
1612             {
1613               queue_deleten(job, 0, i);
1614               i = 0;
1615             }
1616           matched = 1;
1617           /* if all solvables have the same evr */
1618           s = pool_id2solvable(pool, mq.elements[0]);
1619           evrid = s->evr;
1620           for (j = 0; j < mq.count; j++)
1621             {
1622               s = pool_id2solvable(pool, mq.elements[j]);
1623               if (s->evr != evrid)
1624                 break;
1625             }
1626           if (j == mq.count && j > 1)
1627             {
1628               prune_to_best_arch(pool, &mq);
1629               // prune_to_highest_prio(pool, &mq);
1630               mq.count = 1;
1631             }
1632           if (mq.count > 1)
1633             {
1634               job->elements[i] = SOLVER_SOLVABLE_ONE_OF;
1635               job->elements[i + 1] = pool_queuetowhatprovides(pool, &mq);
1636             }
1637           else
1638             {
1639               job->elements[i] = SOLVER_SOLVABLE;
1640               job->elements[i + 1] = mq.elements[0];
1641             }
1642         }
1643       else if (matched)
1644         {
1645           queue_deleten(job, i, 2);
1646           i -= 2;
1647         }
1648     }
1649   queue_free(&mq);
1650   if (matched)
1651     return 1;
1652   if (!archid)
1653     {
1654       char *r;
1655       if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1656         {
1657           *r = 0;
1658           if (limitevr(pool, evr, job, archid))
1659             {
1660               *r = '.';
1661               return 1;
1662             }
1663           *r = '.';
1664         }
1665     }
1666   return 0;
1667 }
1668
1669 void
1670 mkselect(Pool *pool, int mode, char *name, Queue *job)
1671 {
1672   char *r, *r2;
1673   Id archid;
1674
1675   if (*name == '/')
1676     {
1677       Dataiterator di;
1678       Queue q;
1679       int match = 0;
1680
1681       queue_init(&q);
1682       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
1683       while (dataiterator_step(&di))
1684         {
1685           Solvable *s = pool->solvables + di.solvid;
1686           if (!s->repo || !pool_installable(pool, s))
1687             continue;
1688           queue_push(&q, di.solvid);
1689           dataiterator_skip_solvable(&di);
1690         }
1691       dataiterator_free(&di);
1692       if (q.count)
1693         {
1694           printf("[using file list match for '%s']\n", name);
1695           match = 1;
1696           if (q.count > 1)
1697             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
1698           else
1699             queue_push2(job, SOLVER_SOLVABLE, q.elements[0]);
1700         }
1701       queue_free(&q);
1702       if (match)
1703         return;
1704     }
1705   if ((r = strpbrk(name, "<=>")) != 0)
1706     {
1707       /* relation case, support:
1708        * depglob rel
1709        * depglob.rpm rel
1710        */
1711       int rflags = 0;
1712       int nend = r - name;
1713       for (; *r; r++)
1714         {
1715           if (*r == '<')
1716             rflags |= REL_LT;
1717           else if (*r == '=')
1718             rflags |= REL_EQ;
1719           else if (*r == '>')
1720             rflags |= REL_GT;
1721           else
1722             break;
1723         }
1724       while (*r && *r == ' ' && *r == '\t')
1725         r++;
1726       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
1727         nend--;
1728       name[nend] = 0;
1729       if (!*name || !*r)
1730         {
1731           fprintf(stderr, "bad relation\n");
1732           exit(1);
1733         }
1734       if (depglob(pool, name, job))
1735         {
1736           addrelation(pool, job, rflags, str2id(pool, r, 1));
1737           return;
1738         }
1739       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
1740         {
1741           *r2 = 0;
1742           if (depglob(pool, name, job))
1743             {
1744               *r2 = '.';
1745               addrelation(pool, job, REL_ARCH, archid);
1746               addrelation(pool, job, rflags, str2id(pool, r, 1));
1747               return;
1748             }
1749           *r2 = '.';
1750         }
1751     }
1752   else
1753     {
1754       /* no relation case, support:
1755        * depglob
1756        * depglob.arch
1757        * depglob-version-release
1758        * depglob-version-release.arch
1759        */
1760       if (depglob(pool, name, job))
1761         return;
1762       archid = 0;
1763       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
1764         {
1765           *r = 0;
1766           if (depglob(pool, name, job))
1767             {
1768               *r = '.';
1769               addrelation(pool, job, REL_ARCH, archid);
1770               return;
1771             }
1772           *r = '.';
1773         }
1774       if ((r = strrchr(name, '-')) != 0)
1775         {
1776           *r = 0;
1777           if (depglob(pool, name, job))
1778             {
1779               /* have just the version */
1780               *r = '-';
1781               if (limitevr(pool, r + 1, job, 0))
1782                 return;
1783             }
1784           if ((r2 = strrchr(name, '-')) != 0)
1785             {
1786               *r = '-';
1787               *r2 = 0;
1788               if (depglob(pool, name, job))
1789                 {
1790                   *r2 = '-';
1791                   if (limitevr(pool, r2 + 1, job, 0))
1792                     return;
1793                 }
1794               *r2 = '-';
1795             }
1796           *r = '-';
1797         }
1798     }
1799   fprintf(stderr, "nothing matches '%s'\n", name);
1800   exit(1);
1801 }
1802
1803
1804 int
1805 yesno(const char *str)
1806 {
1807   char inbuf[128], *ip;
1808
1809   for (;;)
1810     {
1811       printf("%s", str);
1812       fflush(stdout);
1813       *inbuf = 0;
1814       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1815         {
1816           printf("Abort.\n");
1817           exit(1);
1818         }
1819       while (*ip == ' ' || *ip == '\t')
1820         ip++;
1821       if (*ip == 'q')
1822         {
1823           printf("Abort.\n");
1824           exit(1);
1825         }
1826       if (*ip == 'y' || *ip == 'n')
1827         return *ip == 'y' ? 1 : 0;
1828     }
1829 }
1830
1831 struct fcstate {
1832   FILE **newpkgsfps;
1833   Queue *checkq;
1834   int newpkgscnt;
1835   void *rpmdbstate;
1836 };
1837
1838 static void *
1839 fileconflict_cb(Pool *pool, Id p, void *cbdata)
1840 {
1841   struct fcstate *fcstate = cbdata;
1842   Solvable *s;
1843   Id rpmdbid;
1844   int i;
1845   FILE *fp;
1846
1847   if (!p)
1848     {
1849       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1850       return 0;
1851     }
1852   s = pool_id2solvable(pool, p);
1853   if (pool->installed && s->repo == pool->installed)
1854     {
1855       if (!s->repo->rpmdbid)
1856         return 0;
1857       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1858       if (!rpmdbid)
1859         return 0;
1860        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1861     }
1862   for (i = 0; i < fcstate->newpkgscnt; i++)
1863     if (fcstate->checkq->elements[i] == p)
1864       break;
1865   if (i == fcstate->newpkgscnt)
1866     return 0;
1867   fp = fcstate->newpkgsfps[i];
1868   if (!fp)
1869     return 0;
1870   rewind(fp);
1871   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1872 }
1873
1874 void
1875 runrpm(const char *arg, const char *name, int dupfd3)
1876 {
1877   pid_t pid;
1878   int status;
1879
1880   if ((pid = fork()) == (pid_t)-1)
1881     {
1882       perror("fork");
1883       exit(1);
1884     }
1885   if (pid == 0)
1886     {
1887       if (dupfd3 != -1 && dupfd3 != 3)
1888         {
1889           dup2(dupfd3, 3);
1890           close(dupfd3);
1891         }
1892       if (dupfd3 != -1)
1893         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1894       if (strcmp(arg, "-e") == 0)
1895         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1896       else
1897         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1898       perror("rpm");
1899       _exit(0);
1900     }
1901   while (waitpid(pid, &status, 0) != pid)
1902     ;
1903   if (status)
1904     {
1905       printf("rpm failed\n");
1906       exit(1);
1907     }
1908 }
1909
1910 static Id
1911 nscallback(Pool *pool, void *data, Id name, Id evr)
1912 {
1913   if (name == NAMESPACE_PRODUCTBUDDY)
1914     {    
1915       /* SUSE specific hack: each product has an associated rpm */
1916       Solvable *s = pool->solvables + evr; 
1917       Id p, pp, cap; 
1918       
1919       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1920       if (!cap)
1921         return 0;
1922       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1923       if (!cap)
1924         return 0;
1925       FOR_PROVIDES(p, pp, cap) 
1926         {
1927           Solvable *ps = pool->solvables + p; 
1928           if (ps->repo == s->repo && ps->arch == s->arch)
1929             break;
1930         }
1931       return p;
1932     }
1933   return 0;
1934 }
1935
1936 #ifdef SOFTLOCKS_PATH
1937
1938 void
1939 addsoftlocks(Pool *pool, Queue *job)
1940 {
1941   FILE *fp;
1942   Id type, id, p, pp;
1943   char *bp, *ep, buf[4096];
1944
1945   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
1946     return;
1947   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
1948     {
1949       while (*bp == ' ' || *bp == '\t')
1950         bp++;
1951       if (!*bp || *bp == '#')
1952         continue;
1953       for (ep = bp; *ep; ep++)
1954         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
1955           break;
1956       *ep = 0;
1957       type = SOLVER_SOLVABLE_NAME;
1958       if (!strncmp(bp, "provides:", 9) && bp[9])
1959         {
1960           type = SOLVER_SOLVABLE_PROVIDES;
1961           bp += 9;
1962         }
1963       id = str2id(pool, bp, 1);
1964       if (pool->installed)
1965         {
1966           FOR_JOB_SELECT(p, pp, type, id)
1967             if (pool->solvables[p].repo == pool->installed)
1968               break;
1969           if (p)
1970             continue;   /* ignore, as it is already installed */
1971         }
1972       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
1973     }
1974   fclose(fp);
1975 }
1976
1977 #endif
1978
1979 #define MODE_LIST        0
1980 #define MODE_INSTALL     1
1981 #define MODE_ERASE       2
1982 #define MODE_UPDATE      3
1983 #define MODE_DISTUPGRADE 4
1984 #define MODE_VERIFY      5
1985 #define MODE_PATCH       6
1986 #define MODE_INFO        7
1987 #define MODE_REPOLIST    8
1988 #define MODE_SEARCH      9
1989
1990 void
1991 usage(int r)
1992 {
1993   fprintf(stderr, "Usage: solv COMMAND <select>\n");
1994   fprintf(stderr, "\n");
1995   fprintf(stderr, "    distupgrade: replace installed packages with\n");
1996   fprintf(stderr, "                 versions from the repositories\n");
1997   fprintf(stderr, "    erase:       erase installed packages\n");
1998   fprintf(stderr, "    info:        display package information\n");
1999   fprintf(stderr, "    install:     install packages\n");
2000   fprintf(stderr, "    list:        list packages\n");
2001   fprintf(stderr, "    repos:       list enabled repositories\n");
2002   fprintf(stderr, "    search:      search name/summary/description\n");
2003   fprintf(stderr, "    update:      update installed packages\n");
2004   fprintf(stderr, "\n");
2005   exit(r);
2006 }
2007
2008 int
2009 main(int argc, char **argv)
2010 {
2011   Pool *pool;
2012   Repo *commandlinerepo = 0;
2013   Id *commandlinepkgs = 0;
2014   Id p, pp;
2015   struct repoinfo *repoinfos;
2016   int nrepoinfos = 0;
2017   int mainmode = 0, mode = 0;
2018   int i, newpkgs;
2019   Queue job, checkq;
2020   Solver *solv = 0;
2021   Transaction *trans;
2022   char inbuf[128], *ip;
2023   int allpkgs = 0;
2024   FILE **newpkgsfps;
2025   struct fcstate fcstate;
2026
2027   argc--;
2028   argv++;
2029   if (!argv[0])
2030     usage(1);
2031   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2032     {
2033       mainmode = MODE_INSTALL;
2034       mode = SOLVER_INSTALL;
2035     }
2036   else if (!strcmp(argv[0], "patch"))
2037     {
2038       mainmode = MODE_PATCH;
2039       mode = SOLVER_INSTALL;
2040     }
2041   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2042     {
2043       mainmode = MODE_ERASE;
2044       mode = SOLVER_ERASE;
2045     }
2046   else if (!strcmp(argv[0], "list"))
2047     {
2048       mainmode = MODE_LIST;
2049       mode = 0;
2050     }
2051   else if (!strcmp(argv[0], "info"))
2052     {
2053       mainmode = MODE_INFO;
2054       mode = 0;
2055     }
2056   else if (!strcmp(argv[0], "search"))
2057     {
2058       mainmode = MODE_SEARCH;
2059       mode = 0;
2060     }
2061   else if (!strcmp(argv[0], "verify"))
2062     {
2063       mainmode = MODE_VERIFY;
2064       mode = SOLVER_VERIFY;
2065     }
2066   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2067     {
2068       mainmode = MODE_UPDATE;
2069       mode = SOLVER_UPDATE;
2070     }
2071   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2072     {
2073       mainmode = MODE_DISTUPGRADE;
2074       mode = SOLVER_UPDATE;
2075     }
2076   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2077     {
2078       mainmode = MODE_REPOLIST;
2079       mode = 0;
2080     }
2081   else
2082     usage(1);
2083
2084   pool = pool_create();
2085 #ifdef FEDORA
2086   pool->obsoleteusescolors = 1;
2087 #endif
2088   pool_setloadcallback(pool, load_stub, 0);
2089   pool->nscallback = nscallback;
2090   // pool_setdebuglevel(pool, 2);
2091   setarch(pool);
2092   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
2093
2094   if (mainmode == MODE_REPOLIST)
2095     {
2096       int j = 1;
2097       for (i = 0; i < nrepoinfos; i++)
2098         {
2099           struct repoinfo *cinfo = repoinfos + i;
2100           if (!cinfo->enabled)
2101             continue;
2102           printf("%d: %-20s %s\n", j++, cinfo->alias, cinfo->name);
2103         }
2104       exit(0);
2105     }
2106
2107   read_repos(pool, repoinfos, nrepoinfos);
2108
2109   if (mainmode == MODE_SEARCH)
2110     {
2111       Dataiterator di;
2112       Map m;
2113       if (argc != 2)
2114         usage(1);
2115       map_init(&m, pool->nsolvables);
2116       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2117       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2118       dataiterator_set_search(&di, 0, 0);
2119       while (dataiterator_step(&di))
2120         MAPSET(&m, di.solvid);
2121       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2122       dataiterator_set_search(&di, 0, 0);
2123       while (dataiterator_step(&di))
2124         MAPSET(&m, di.solvid);
2125       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2126       dataiterator_set_search(&di, 0, 0);
2127       while (dataiterator_step(&di))
2128         MAPSET(&m, di.solvid);
2129       dataiterator_free(&di);
2130
2131       for (p = 1; p < pool->nsolvables; p++)
2132         {
2133           Solvable *s = pool_id2solvable(pool, p);
2134           if (!MAPTST(&m, p))
2135             continue;
2136           printf("  - %s: %s\n", solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2137         }
2138       map_free(&m);
2139       exit(0);
2140     }
2141
2142
2143   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2144     {
2145       for (i = 1; i < argc; i++)
2146         {
2147           int l;
2148           l = strlen(argv[i]);
2149           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2150             continue;
2151           if (access(argv[i], R_OK))
2152             {
2153               perror(argv[i]);
2154               exit(1);
2155             }
2156           if (!commandlinepkgs)
2157             commandlinepkgs = sat_calloc(argc, sizeof(Id));
2158           if (!commandlinerepo)
2159             commandlinerepo = repo_create(pool, "@commandline");
2160           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2161           commandlinepkgs[i] = commandlinerepo->end - 1;
2162         }
2163       if (commandlinerepo)
2164         repo_internalize(commandlinerepo);
2165     }
2166
2167   // FOR_REPOS(i, repo)
2168   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2169   pool_addfileprovides(pool);
2170   pool_createwhatprovides(pool);
2171
2172   queue_init(&job);
2173   for (i = 1; i < argc; i++)
2174     {
2175       Queue job2;
2176       int j;
2177
2178       if (commandlinepkgs && commandlinepkgs[i])
2179         {
2180           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2181           continue;
2182         }
2183       queue_init(&job2);
2184       mkselect(pool, mode, argv[i], &job2);
2185       for (j = 0; j < job2.count; j++)
2186         queue_push(&job, job2.elements[j]);
2187       queue_free(&job2);
2188     }
2189
2190   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2191     {
2192       printf("no package matched\n");
2193       exit(1);
2194     }
2195
2196   if (!job.count)
2197     allpkgs = 1;
2198
2199   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2200     {
2201       /* list mode, no solver needed */
2202       for (i = 0; i < job.count; i += 2)
2203         {
2204           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2205             {
2206               Solvable *s = pool_id2solvable(pool, p);
2207               if (mainmode == MODE_INFO)
2208                 {
2209                   printf("Name:        %s\n", solvable2str(pool, s));
2210                   printf("Repo:        %s\n", s->repo->name);
2211                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2212                   printf("Url:         %s\n", solvable_lookup_str(s, SOLVABLE_URL));
2213                   printf("License:     %s\n", solvable_lookup_str(s, SOLVABLE_LICENSE));
2214                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2215                   printf("\n");
2216                 }
2217               else
2218                 {
2219                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
2220                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2221                   if (sum)
2222                     printf("    %s\n", sum);
2223                 }
2224             }
2225         }
2226       queue_free(&job);
2227       pool_free(pool);
2228       free_repoinfos(repoinfos, nrepoinfos);
2229       sat_free(commandlinepkgs);
2230       exit(0);
2231     }
2232
2233   if (mainmode == MODE_PATCH)
2234     {
2235       int pruneyou = 0;
2236       Map installedmap;
2237       Solvable *s;
2238
2239       map_init(&installedmap, pool->nsolvables);
2240       if (pool->installed)
2241         FOR_REPO_SOLVABLES(pool->installed, p, s)
2242           MAPSET(&installedmap, p);
2243
2244       /* install all patches */
2245       for (p = 1; p < pool->nsolvables; p++)
2246         {
2247           const char *type;
2248           int r;
2249           Id p2;
2250
2251           s = pool->solvables + p;
2252           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2253             continue;
2254           FOR_PROVIDES(p2, pp, s->name)
2255             {
2256               Solvable *s2 = pool->solvables + p2;
2257               if (s2->name != s->name)
2258                 continue;
2259               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2260               if (r < 0 || (r == 0 && p > p2))
2261                 break;
2262             }
2263           if (p2)
2264             continue;
2265           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2266           if (type && !strcmp(type, "optional"))
2267             continue;
2268           r = solvable_trivial_installable_map(s, &installedmap, 0);
2269           if (r == -1)
2270             continue;
2271           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2272             {
2273               if (!pruneyou++)
2274                 queue_empty(&job);
2275             }
2276           else if (pruneyou)
2277             continue;
2278           queue_push2(&job, SOLVER_SOLVABLE, p);
2279         }
2280       map_free(&installedmap);
2281     }
2282
2283   // add mode
2284   for (i = 0; i < job.count; i += 2)
2285     {
2286       if (mode == SOLVER_UPDATE)
2287         {
2288           /* make update of not installed packages an install */
2289           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2290             if (pool->installed && pool->solvables[p].repo == pool->installed)
2291               break;
2292           if (!p)
2293             {
2294               job.elements[i] |= SOLVER_INSTALL;
2295               continue;
2296             }
2297         }
2298       job.elements[i] |= mode;
2299     }
2300
2301   // multiversion test
2302   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2303   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2304   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2305
2306 #ifdef SOFTLOCKS_PATH
2307   addsoftlocks(pool, &job);
2308 #endif
2309
2310 rerunsolver:
2311   for (;;)
2312     {
2313       Id problem, solution;
2314       int pcnt, scnt;
2315
2316       solv = solver_create(pool);
2317       solv->ignorealreadyrecommended = 1;
2318       solv->updatesystem = allpkgs && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
2319       solv->dosplitprovides = solv->updatesystem;
2320       solv->fixsystem = allpkgs && (mainmode == MODE_VERIFY);
2321       if (mainmode == MODE_DISTUPGRADE)
2322         {
2323           solv->distupgrade = 1;
2324           solv->allowdowngrade = 1;
2325           solv->allowarchchange = 1;
2326           solv->allowvendorchange = 1;
2327         }
2328       if (mainmode == MODE_ERASE)
2329         solv->allowuninstall = 1;       /* don't nag */
2330
2331       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
2332       solver_solve(solv, &job);
2333       if (!solv->problems.count)
2334         break;
2335       pcnt = solver_problem_count(solv);
2336       printf("Found %d problems:\n", pcnt);
2337       for (problem = 1; problem <= pcnt; problem++)
2338         {
2339           int take = 0;
2340           printf("Problem %d:\n", problem);
2341           solver_printprobleminfo(solv, problem);
2342           printf("\n");
2343           scnt = solver_solution_count(solv, problem);
2344           for (solution = 1; solution <= scnt; solution++)
2345             {
2346               printf("Solution %d:\n", solution);
2347               solver_printsolution(solv, problem, solution);
2348               printf("\n");
2349             }
2350           for (;;)
2351             {
2352               printf("Please choose a solution: ");
2353               fflush(stdout);
2354               *inbuf = 0;
2355               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2356                 {
2357                   printf("Abort.\n");
2358                   exit(1);
2359                 }
2360               while (*ip == ' ' || *ip == '\t')
2361                 ip++;
2362               if (*ip >= '0' && *ip <= '9')
2363                 {
2364                   take = atoi(ip);
2365                   if (take >= 1 && take <= scnt)
2366                     break;
2367                 }
2368               if (*ip == 's')
2369                 {
2370                   take = 0;
2371                   break;
2372                 }
2373               if (*ip == 'q')
2374                 {
2375                   printf("Abort.\n");
2376                   exit(1);
2377                 }
2378             }
2379           if (!take)
2380             continue;
2381           solver_take_solution(solv, problem, take, &job);
2382         }
2383       solver_free(solv);
2384       solv = 0;
2385     }
2386
2387   trans = &solv->trans;
2388   if (!trans->steps.count)
2389     {
2390       printf("Nothing to do.\n");
2391       exit(1);
2392     }
2393   printf("\n");
2394   printf("Transaction summary:\n\n");
2395   solver_printtransaction(solv);
2396
2397 #ifndef FEDORA
2398   if (1)
2399     {
2400       DUChanges duc[4];
2401       int i;
2402
2403       duc[0].path = "/";
2404       duc[1].path = "/usr/share/man";
2405       duc[2].path = "/sbin";
2406       duc[3].path = "/etc";
2407       transaction_calc_duchanges(trans, duc, 4);
2408       for (i = 0; i < 4; i++)
2409         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
2410     }
2411 #endif
2412   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
2413   printf("\n");
2414
2415   if (!yesno("OK to continue (y/n)? "))
2416     {
2417       printf("Abort.\n");
2418       exit(1);
2419     }
2420
2421   queue_init(&checkq);
2422   newpkgs = transaction_installedresult(trans, &checkq);
2423   newpkgsfps = 0;
2424
2425   if (newpkgs)
2426     {
2427       int downloadsize = 0;
2428       for (i = 0; i < newpkgs; i++)
2429         {
2430           Solvable *s;
2431
2432           p = checkq.elements[i];
2433           s = pool_id2solvable(pool, p);
2434           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
2435         }
2436       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
2437       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
2438       for (i = 0; i < newpkgs; i++)
2439         {
2440           unsigned int medianr;
2441           char *loc;
2442           Solvable *s;
2443           struct repoinfo *cinfo;
2444           const unsigned char *chksum;
2445           Id chksumtype;
2446           Dataiterator di;
2447
2448           p = checkq.elements[i];
2449           s = pool_id2solvable(pool, p);
2450           if (s->repo == commandlinerepo)
2451             {
2452               loc = solvable_get_location(s, &medianr);
2453               if (!(newpkgsfps[i] = fopen(loc, "r")))
2454                 {
2455                   perror(loc);
2456                   exit(1);
2457                 }
2458               putchar('.');
2459               continue;
2460             }
2461           cinfo = s->repo->appdata;
2462           if (!cinfo)
2463             {
2464               printf("%s: no repository information\n", s->repo->name);
2465               exit(1);
2466             }
2467           loc = solvable_get_location(s, &medianr);
2468           if (!loc)
2469              continue;
2470
2471           if (pool->installed && pool->installed->nsolvables)
2472             {
2473               /* try a delta first */
2474               char *matchname = strdup(id2str(pool, s->name));
2475               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
2476               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
2477               while (dataiterator_step(&di))
2478                 {
2479                   Id baseevr, op;
2480
2481                   dataiterator_setpos_parent(&di);
2482                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
2483                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
2484                     continue;
2485                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
2486                   FOR_PROVIDES(op, pp, s->name)
2487                     {
2488                       Solvable *os = pool->solvables + op;
2489                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
2490                         break;
2491                     }
2492                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
2493                     {
2494                       /* base is installed, run sequence check */
2495                       const char *seqname;
2496                       const char *seqevr;
2497                       const char *seqnum;
2498                       const char *seq;
2499                       const char *dloc;
2500                       FILE *fp;
2501                       char cmd[128];
2502                       int newfd;
2503
2504                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
2505                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
2506                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
2507                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
2508                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
2509 #ifdef FEDORA
2510                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
2511 #else
2512                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
2513 #endif
2514                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
2515                         continue;       /* didn't match */
2516                       /* looks good, download delta */
2517                       chksumtype = 0;
2518                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
2519                       if (!chksumtype)
2520                         continue;       /* no way! */
2521                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
2522                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
2523                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
2524                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
2525                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
2526                         continue;
2527                       /* got it, now reconstruct */
2528                       newfd = opentmpfile();
2529 #ifdef FEDORA
2530                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
2531 #else
2532                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
2533 #endif
2534                       fcntl(fileno(fp), F_SETFD, 0);
2535                       if (system(cmd))
2536                         {
2537                           close(newfd);
2538                           fclose(fp);
2539                           continue;
2540                         }
2541                       lseek(newfd, 0, SEEK_SET);
2542                       chksumtype = 0;
2543                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2544                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
2545                         {
2546                           close(newfd);
2547                           fclose(fp);
2548                           continue;
2549                         }
2550                       newpkgsfps[i] = fdopen(newfd, "r");
2551                       fclose(fp);
2552                       break;
2553                     }
2554                 }
2555               dataiterator_free(&di);
2556               sat_free(matchname);
2557             }
2558           
2559           if (newpkgsfps[i])
2560             {
2561               putchar('d');
2562               fflush(stdout);
2563               continue;         /* delta worked! */
2564             }
2565           if (cinfo->type == TYPE_SUSETAGS)
2566             {
2567               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
2568               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
2569             }
2570           chksumtype = 0;
2571           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
2572           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
2573             {
2574               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
2575               exit(1);
2576             }
2577           putchar('.');
2578           fflush(stdout);
2579         }
2580       putchar('\n');
2581     }
2582
2583   if (newpkgs)
2584     {
2585       Queue conflicts;
2586
2587       printf("Searching for file conflicts\n");
2588       queue_init(&conflicts);
2589       fcstate.rpmdbstate = 0;
2590       fcstate.newpkgscnt = newpkgs;
2591       fcstate.checkq = &checkq;
2592       fcstate.newpkgsfps = newpkgsfps;
2593       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
2594       if (conflicts.count)
2595         {
2596           printf("\n");
2597           for (i = 0; i < conflicts.count; i += 5)
2598             printf("file %s of package %s conflicts with package %s\n", id2str(pool, conflicts.elements[i]), solvid2str(pool, conflicts.elements[i + 1]), solvid2str(pool, conflicts.elements[i + 3]));
2599           printf("\n");
2600           if (yesno("Re-run solver (y/n/q)? "))
2601             {
2602               for (i = 0; i < newpkgs; i++)
2603                 if (newpkgsfps[i])
2604                   fclose(newpkgsfps[i]);
2605               newpkgsfps = sat_free(newpkgsfps);
2606               solver_free(solv);
2607               pool_add_fileconflicts_deps(pool, &conflicts);
2608               pool_createwhatprovides(pool);    /* Hmm... */
2609               goto rerunsolver;
2610             }
2611         }
2612       queue_free(&conflicts);
2613     }
2614
2615   printf("Committing transaction:\n\n");
2616   transaction_order(trans, 0);
2617   for (i = 0; i < trans->steps.count; i++)
2618     {
2619       const char *evr, *evrp, *nvra;
2620       Solvable *s;
2621       int j;
2622       FILE *fp;
2623
2624       p = trans->steps.elements[i];
2625       s = pool_id2solvable(pool, p);
2626       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
2627       switch(type)
2628         {
2629         case SOLVER_TRANSACTION_ERASE:
2630           printf("erase %s\n", solvid2str(pool, p));
2631           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
2632             continue;
2633           /* strip epoch from evr */
2634           evr = evrp = id2str(pool, s->evr);
2635           while (*evrp >= '0' && *evrp <= '9')
2636             evrp++;
2637           if (evrp > evr && evrp[0] == ':' && evrp[1])
2638             evr = evrp + 1;
2639           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
2640           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
2641           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
2642           break;
2643         case SOLVER_TRANSACTION_INSTALL:
2644         case SOLVER_TRANSACTION_MULTIINSTALL:
2645           printf("install %s\n", solvid2str(pool, p));
2646           for (j = 0; j < newpkgs; j++)
2647             if (checkq.elements[j] == p)
2648               break;
2649           fp = j < newpkgs ? newpkgsfps[j] : 0;
2650           if (!fp)
2651             continue;
2652           rewind(fp);
2653           lseek(fileno(fp), 0, SEEK_SET);
2654           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
2655           fclose(fp);
2656           newpkgsfps[j] = 0;
2657           break;
2658         default:
2659           break;
2660         }
2661     }
2662
2663   for (i = 0; i < newpkgs; i++)
2664     if (newpkgsfps[i])
2665       fclose(newpkgsfps[i]);
2666   sat_free(newpkgsfps);
2667   queue_free(&checkq);
2668   solver_free(solv);
2669   queue_free(&job);
2670   pool_free(pool);
2671   free_repoinfos(repoinfos, nrepoinfos);
2672   sat_free(commandlinepkgs);
2673   exit(0);
2674 }