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