- support fedora style yum repos (var subst, mirrorlist)
[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 <unistd.h>
22 #include <zlib.h>
23 #include <fcntl.h>
24 #include <assert.h>
25 #include <sys/utsname.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28
29 #include "pool.h"
30 #include "poolarch.h"
31 #include "repo.h"
32 #include "evr.h"
33 #include "util.h"
34 #include "solver.h"
35 #include "solverdebug.h"
36 #include "chksum.h"
37 #include "repo_solv.h"
38
39 #include "repo_write.h"
40 #include "repo_rpmdb.h"
41 #include "repo_products.h"
42 #include "repo_rpmmd.h"
43 #include "repo_susetags.h"
44 #include "repo_repomdxml.h"
45 #include "repo_updateinfoxml.h"
46 #include "repo_deltainfoxml.h"
47 #include "repo_content.h"
48 #include "pool_fileconflicts.h"
49
50
51 #ifdef FEDORA
52 # define REPOINFO_PATH "/etc/yum.repos.d"
53 #else
54 # define REPOINFO_PATH "/etc/zypp/repos.d"
55 # define PRODUCTS_PATH "/etc/products.d"
56 #endif
57
58 #define SOLVCACHE_PATH "/var/cache/solv"
59
60
61 struct repoinfo {
62   Repo *repo;
63
64   char *alias;
65   char *name;
66   int enabled;
67   int autorefresh;
68   char *baseurl;
69   char *metalink;
70   char *path;
71   int type;
72   int gpgcheck;
73   int priority;
74   int keeppackages;
75 };
76
77 #ifdef FEDORA
78 char *
79 yum_substitute(Pool *pool, char *line)
80 {
81   char *p, *p2;
82
83   p = line;
84   while ((p2 = strchr(p, '$')) != 0)
85     {
86       if (!strncmp(p2, "$releasever", 11))
87         {
88           static char *releaseevr;
89           if (!releaseevr)
90             {
91               FILE *fp;
92               char buf[1024], *bp;
93
94               fp = popen("rpm --nodigest --nosignature -q --qf '%{VERSION}\n' --whatprovides redhat-release", "r");
95               fread(buf, 1, sizeof(buf), fp);
96               fclose(fp);
97               bp = buf;
98               while (*bp != ' ' && *bp != '\t' && *bp != '\n')
99                 bp++;
100               *bp = 0;
101               releaseevr = strdup(buf);
102             }
103           *p2 = 0;
104           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
105           p2 = p + (p2 - line);
106           line = p;
107           p = p2 + strlen(releaseevr);
108           continue;
109         }
110       if (!strncmp(p2, "$basearch", 9))
111         {
112           static char *basearch;
113           if (!basearch)
114             {
115               struct utsname un;
116               if (uname(&un))
117                 {
118                   perror("uname");
119                   exit(1);
120                 }
121               basearch = strdup(un.machine);
122               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
123                 basearch[1] = '3';
124             }
125           *p2 = 0;
126           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
127           p2 = p + (p2 - line);
128           line = p;
129           p = p2 + strlen(basearch);
130           continue;
131         }
132       p = p2 + 1;
133     }
134   return line;
135 }
136 #endif
137
138 #define TYPE_UNKNOWN    0
139 #define TYPE_SUSETAGS   1
140 #define TYPE_RPMMD      2
141 #define TYPE_PLAINDIR   3
142
143 static int
144 read_repoinfos_sort(const void *ap, const void *bp)
145 {
146   const struct repoinfo *a = ap;
147   const struct repoinfo *b = bp;
148   return strcmp(a->alias, b->alias);
149 }
150
151 struct repoinfo *
152 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
153 {
154   char buf[4096];
155   char buf2[4096], *kp, *vp, *kpe;
156   DIR *dir;
157   FILE *fp;
158   struct dirent *ent;
159   int l, rdlen;
160   struct repoinfo *repoinfos = 0, *cinfo;
161   int nrepoinfos = 0;
162
163   rdlen = strlen(reposdir);
164   dir = opendir(reposdir);
165   if (!dir)
166     {
167       *nrepoinfosp = 0;
168       return 0;
169     }
170   while ((ent = readdir(dir)) != 0)
171     {
172       l = strlen(ent->d_name);
173       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
174         continue;
175       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
176       if ((fp = fopen(buf, "r")) == 0)
177         {
178           perror(buf);
179           continue;
180         }
181       cinfo = 0;
182       while(fgets(buf2, sizeof(buf2), fp))
183         {
184           l = strlen(buf2);
185           if (l == 0)
186             continue;
187           while (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t')
188             buf2[--l] = 0;
189           kp = buf2;
190           while (*kp == ' ' || *kp == '\t')
191             kp++;
192           if (!*kp || *kp == '#')
193             continue;
194           if (strchr(kp, '$'))
195             kp = yum_substitute(pool, kp);
196           if (*kp == '[')
197             {
198               vp = strrchr(kp, ']');
199               if (!vp)
200                 continue;
201               *vp = 0;
202               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
203               cinfo = repoinfos + nrepoinfos++;
204               memset(cinfo, 0, sizeof(*cinfo));
205               cinfo->alias = strdup(kp + 1);
206               cinfo->type = TYPE_RPMMD;
207               cinfo->autorefresh = 1;
208               continue;
209             }
210           if (!cinfo)
211             continue;
212           vp = strchr(kp, '=');
213           if (!vp)
214             continue;
215           for (kpe = vp - 1; kpe >= kp; kpe--)
216             if (*kpe != ' ' && *kpe != '\t')
217               break;
218           if (kpe == kp)
219             continue;
220           vp++;
221           while (*vp == ' ' || *vp == '\t')
222             vp++;
223           kpe[1] = 0;
224           if (!strcmp(kp, "name"))
225             cinfo->name = strdup(vp);
226           else if (!strcmp(kp, "enabled"))
227             cinfo->enabled = *vp == '0' ? 0 : 1;
228           else if (!strcmp(kp, "autorefresh"))
229             cinfo->autorefresh = *vp == '0' ? 0 : 1;
230           else if (!strcmp(kp, "gpgcheck"))
231             cinfo->gpgcheck = *vp == '0' ? 0 : 1;
232           else if (!strcmp(kp, "baseurl"))
233             cinfo->baseurl = strdup(vp);
234           else if (!strcmp(kp, "mirrorlist"))
235             {
236               if (strstr(vp, "metalink"))
237                 cinfo->metalink = strdup(vp);
238             }
239           else if (!strcmp(kp, "path"))
240             {
241               if (vp && strcmp(vp, "/") != 0)
242                 cinfo->path = strdup(vp);
243             }
244           else if (!strcmp(kp, "type"))
245             {
246               if (!strcmp(vp, "yast2"))
247                 cinfo->type = TYPE_SUSETAGS;
248               else if (!strcmp(vp, "rpm-md"))
249                 cinfo->type = TYPE_RPMMD;
250               else if (!strcmp(vp, "plaindir"))
251                 cinfo->type = TYPE_PLAINDIR;
252               else
253                 cinfo->type = TYPE_UNKNOWN;
254             }
255           else if (!strcmp(kp, "priority"))
256             cinfo->priority = atoi(vp);
257           else if (!strcmp(kp, "keeppackages"))
258             cinfo->keeppackages = *vp == '0' ? 0 : 1;
259         }
260       fclose(fp);
261       cinfo = 0;
262     }
263   closedir(dir);
264   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
265   *nrepoinfosp = nrepoinfos;
266   return repoinfos;
267 }
268
269 static ssize_t
270 cookie_gzread(void *cookie, char *buf, size_t nbytes)
271 {
272   return gzread((gzFile *)cookie, buf, nbytes);
273 }
274
275 static int
276 cookie_gzclose(void *cookie)
277 {
278   return gzclose((gzFile *)cookie);
279 }
280
281 static inline int
282 opentmpfile()
283 {
284   char tmpl[100];
285   int fd;
286
287   strcpy(tmpl, "/var/tmp/solvXXXXXX");
288   fd = mkstemp(tmpl);
289   if (fd < 0)
290     {
291       perror("mkstemp");
292       exit(1);
293     }
294   unlink(tmpl);
295   return fd;
296 }
297
298 static int
299 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
300 {
301   char buf[1024];
302   unsigned char *sum;
303   void *h;
304   int l;
305
306   h = sat_chksum_create(chksumtype);
307   if (!h)
308     {
309       printf("%s: unknown checksum type\n", file);
310       return 0;
311     }
312   while ((l = read(fd, buf, sizeof(buf))) > 0)
313     sat_chksum_add(h, buf, l);
314   lseek(fd, 0, SEEK_SET);
315   l = 0;
316   sum = sat_chksum_get(h, &l);
317   if (memcmp(sum, chksum, l))
318     {
319       printf("%s: checksum mismatch\n", file);
320       return 0;
321     }
322   return 1;
323 }
324
325 char *
326 findmetalinkurl(FILE *fp)
327 {
328   char buf[4096], *bp, *ep;
329   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
330     {
331       while (*bp == ' ' || *bp == '\t')
332         bp++;
333       if (strncmp(bp, "<url", 4))
334         continue;
335       bp = strchr(bp, '>');
336       if (!bp)
337         continue;
338       bp++;
339       ep = strstr(bp, "repodata/repomd.xml</url>");
340       if (!ep)
341         continue;
342       *ep = 0;
343       if (strncmp(bp, "http", 4))
344         continue;
345       return strdup(bp);
346     }
347   return 0;
348 }
349
350 FILE *
351 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype)
352 {
353   pid_t pid;
354   int fd, l;
355   int status;
356   char url[4096];
357   const char *baseurl = cinfo->baseurl;
358
359   if (!baseurl)
360     {
361       if (!cinfo->metalink)
362         return 0;
363       if (file != cinfo->metalink)
364         {
365           FILE *fp = curlfopen(cinfo, cinfo->metalink, 0, 0, 0);
366           if (!fp)
367             return 0;
368           cinfo->baseurl = findmetalinkurl(fp);
369           fclose(fp);
370           if (!cinfo->baseurl)
371             return 0;
372           return curlfopen(cinfo, file, uncompress, chksum, chksumtype);
373         }
374       snprintf(url, sizeof(url), "%s", file);
375     }
376   else
377     {
378       l = strlen(baseurl);
379       if (l && baseurl[l - 1] == '/')
380         snprintf(url, sizeof(url), "%s%s", baseurl, file);
381       else
382         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
383     }
384   fd = opentmpfile();
385   if ((pid = fork()) == (pid_t)-1)
386     {
387       perror("fork");
388       exit(1);
389     }
390   if (pid == 0)
391     {
392       if (fd != 1)
393         {
394           dup2(fd, 1);
395           close(fd);
396         }
397       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
398       perror("curl");
399       _exit(0);
400     }
401   while (waitpid(pid, &status, 0) != pid)
402     ;
403   if (lseek(fd, 0, SEEK_END) == 0)
404     {
405       /* empty file */
406       close(fd);
407       return 0;
408     }
409   lseek(fd, 0, SEEK_SET);
410   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
411     {
412       close(fd);
413       return 0;
414     }
415   if (uncompress)
416     {
417       char tmpl[100];
418       cookie_io_functions_t cio;
419       gzFile *gzf;
420
421       sprintf(tmpl, "/dev/fd/%d", fd);
422       gzf = gzopen(tmpl, "r");
423       close(fd);
424       if (!gzf)
425         return 0;
426       memset(&cio, 0, sizeof(cio));
427       cio.read = cookie_gzread;
428       cio.close = cookie_gzclose;
429       return fopencookie(gzf, "r", cio);
430     }
431   fcntl(fd, F_SETFD, FD_CLOEXEC);
432   return fdopen(fd, "r");
433 }
434
435 void
436 cleanupgpg(char *gpgdir)
437 {
438   char cmd[256];
439   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
440   unlink(cmd);
441   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
442   unlink(cmd);
443   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
444   unlink(cmd);
445   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
446   unlink(cmd);
447   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
448   unlink(cmd);
449   rmdir(gpgdir);
450 }
451
452 int
453 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
454 {
455   char *gpgdir;
456   char *keysfile;
457   const char *pubkey;
458   char cmd[256];
459   FILE *kfp;
460   Solvable *s;
461   Id p;
462   off_t posfp, possigfp;
463   int r, nkeys;
464
465   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
466   if (!gpgdir)
467     return 0;
468   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
469   if (!(kfp = fopen(keysfile, "w")) )
470     {
471       cleanupgpg(gpgdir);
472       return 0;
473     }
474   nkeys = 0;
475   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
476     {
477       if (!s->repo)
478         continue;
479       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
480       if (!pubkey || !*pubkey)
481         continue;
482       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
483         break;
484       if (fputc('\n', kfp) == EOF)      /* Just in case... */
485         break;
486       nkeys++;
487     }
488   if (fclose(kfp) || !nkeys)
489     {
490       cleanupgpg(gpgdir);
491       return 0;
492     }
493   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --import %s", gpgdir, keysfile);
494   if (system(cmd))
495     {
496       fprintf(stderr, "key import error\n");
497       cleanupgpg(gpgdir);
498       return 0;
499     }
500   unlink(keysfile);
501   posfp = lseek(fileno(fp), 0, SEEK_CUR);
502   lseek(fileno(fp), 0, SEEK_SET);
503   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
504   lseek(fileno(sigfp), 0, SEEK_SET);
505   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
506   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
507   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
508   r = system(cmd);
509   lseek(fileno(sigfp), possigfp, SEEK_SET);
510   lseek(fileno(fp), posfp, SEEK_SET);
511   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
512   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
513   cleanupgpg(gpgdir);
514   return r == 0 ? 1 : 0;
515 }
516
517 #define CHKSUM_IDENT "1.1"
518
519 void
520 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
521 {
522   char buf[4096];
523   void *h = sat_chksum_create(chktype);
524   int l;
525
526   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
527   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
528     sat_chksum_add(h, buf, l);
529   rewind(fp);
530   sat_chksum_free(h, out);
531 }
532
533 void
534 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
535 {
536   void *h = sat_chksum_create(chktype);
537   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
538   sat_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
539   sat_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
540   sat_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
541   sat_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
542   sat_chksum_free(h, out);
543 }
544
545 void
546 setarch(Pool *pool)
547 {
548   struct utsname un;
549   if (uname(&un))
550     {
551       perror("uname");
552       exit(1);
553     }
554   pool_setarch(pool, un.machine);
555 }
556
557 char *calccachepath(Repo *repo)
558 {
559   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
560   p = pool_tmpjoin(repo->pool, p, ".solv", 0);
561   q = p + strlen(SOLVCACHE_PATH) + 1;
562   if (*q == '.')
563     *q = '_';
564   for (; *q; q++)
565     if (*q == '/')
566       *q = '_';
567   return p;
568 }
569
570 int
571 usecachedrepo(Repo *repo, unsigned char *cookie)
572 {
573   FILE *fp;
574   unsigned char mycookie[32];
575
576   if (!(fp = fopen(calccachepath(repo), "r")))
577     return 0;
578   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
579     {
580       fclose(fp);
581       return 0;
582     }
583   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
584     {
585       fclose(fp);
586       return 0;
587     }
588   rewind(fp);
589   if (repo_add_solv(repo, fp))
590     {
591       fclose(fp);
592       return 0;
593     }
594   fclose(fp);
595   return 1;
596 }
597
598 void
599 writecachedrepo(Repo *repo, unsigned char *cookie)
600 {
601   Id *addedfileprovides = 0;
602   FILE *fp;
603   int i, fd;
604   char *tmpl;
605   Repodata *info;
606
607   mkdir(SOLVCACHE_PATH, 0755);
608   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
609   fd = mkstemp(tmpl);
610   if (!fd)
611     {
612       free(tmpl);
613       return;
614     }
615   fchmod(fd, 0444);
616   if (!(fp = fdopen(fd, "w")))
617     {
618       close(fd);
619       unlink(tmpl);
620       free(tmpl);
621       return;
622     }
623   info = repo_add_repodata(repo, 0);
624   pool_addfileprovides_ids(repo->pool, 0, &addedfileprovides);
625   if (addedfileprovides && *addedfileprovides)
626     {
627       for (i = 0; addedfileprovides[i]; i++)
628         repodata_add_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[i]);
629     }
630   sat_free(addedfileprovides);
631   repodata_internalize(info);
632   repo_write(repo, fp, 0, 0, 0);
633   repo_free_repodata(repo, info);
634   if (fwrite(cookie, 32, 1, fp) != 1)
635     {
636       fclose(fp);
637       unlink(tmpl);
638       free(tmpl);
639       return;
640     }
641   if (fclose(fp))
642     {
643       unlink(tmpl);
644       free(tmpl);
645       return;
646     }
647   if (!rename(tmpl, calccachepath(repo)))
648     unlink(tmpl);
649   free(tmpl);
650 }
651
652 static Pool *
653 read_sigs()
654 {
655   Pool *sigpool = pool_create();
656   Repo *repo = repo_create(sigpool, "rpmdbkeys");
657   repo_add_rpmdb_pubkeys(repo, 0, 0);
658   return sigpool;
659 }
660
661 static inline int
662 iscompressed(const char *name)
663 {
664   int l = strlen(name);
665   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
666 }
667
668 static inline const char *
669 findinrepomd(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
670 {
671   Pool *pool = repo->pool;
672   Dataiterator di;
673   const char *filename;
674
675   filename = 0;
676   *chksump = 0;
677   *chksumtypep = 0;
678   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
679   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
680   if (dataiterator_step(&di))
681     {
682       dataiterator_setpos_parent(&di);
683       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
684       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
685     }
686   dataiterator_free(&di);
687   if (filename && !*chksumtypep)
688     {
689       printf("no %s file checksum!\n", what);
690       filename = 0;
691     }
692   return filename;
693 }
694
695 void
696 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
697 {
698   Repo *repo;
699   struct repoinfo *cinfo;
700   int i;
701   FILE *fp;
702   FILE *sigfp;
703   Dataiterator di;
704   const char *filename;
705   const unsigned char *filechksum;
706   Id filechksumtype;
707   const char *descrdir;
708   int defvendor;
709   struct stat stb;
710   unsigned char cookie[32];
711   Pool *sigpool = 0;
712
713   repo = repo_create(pool, "@System");
714   printf("rpm database:");
715   if (stat("/var/lib/rpm/Packages", &stb))
716     memset(&stb, 0, sizeof(&stb));
717   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie);
718   if (usecachedrepo(repo, cookie))
719     printf(" cached\n");
720   else
721     {
722       FILE *ofp;
723       printf(" reading\n");
724       int done = 0;
725
726 #ifdef PRODUCTS_PATH
727       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
728 #endif
729       if ((ofp = fopen(calccachepath(repo), "r")) != 0)
730         {
731           Repo *ref = repo_create(pool, "@System.old");
732           if (!repo_add_solv(ref, ofp))
733             {
734               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
735               done = 1;
736             }
737           fclose(ofp);
738           repo_free(ref, 1);
739         }
740       if (!done)
741         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
742       writecachedrepo(repo, cookie);
743     }
744   pool_set_installed(pool, repo);
745
746   for (i = 0; i < nrepoinfos; i++)
747     {
748       cinfo = repoinfos + i;
749       if (!cinfo->enabled)
750         continue;
751
752       repo = repo_create(pool, cinfo->alias);
753       cinfo->repo = repo;
754       repo->appdata = cinfo;
755       repo->priority = 99 - cinfo->priority;
756
757       if (!cinfo->autorefresh && usecachedrepo(repo, 0))
758         {
759           printf("repo '%s':", cinfo->alias);
760           printf(" cached\n");
761           continue;
762         }
763       switch (cinfo->type)
764         {
765         case TYPE_RPMMD:
766           printf("rpmmd repo '%s':", cinfo->alias);
767           fflush(stdout);
768           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0)) == 0)
769             {
770               printf(" no repomd.xml file, skipped\n");
771               repo_free(repo, 1);
772               cinfo->repo = 0;
773               break;
774             }
775           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
776           if (usecachedrepo(repo, cookie))
777             {
778               printf(" cached\n");
779               fclose(fp);
780               break;
781             }
782           sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0);
783 #ifndef FEDORA
784           if (!sigfp)
785             {
786               printf(" unsigned, skipped\n");
787               fclose(fp);
788               break;
789             }
790 #endif
791           if (sigfp)
792             {
793               if (!sigpool)
794                 sigpool = read_sigs();
795               if (!checksig(sigpool, fp, sigfp))
796                 {
797                   printf(" checksig failed, skipped\n");
798                   fclose(sigfp);
799                   fclose(fp);
800                   break;
801                 }
802               fclose(sigfp);
803             }
804           repo_add_repomdxml(repo, fp, 0);
805           fclose(fp);
806           printf(" reading\n");
807           filename = findinrepomd(repo, "primary", &filechksum, &filechksumtype);
808           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
809             {
810               repo_add_rpmmd(repo, fp, 0, 0);
811               fclose(fp);
812             }
813
814           filename = findinrepomd(repo, "updateinfo", &filechksum, &filechksumtype);
815           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
816             {
817               repo_add_updateinfoxml(repo, fp, 0);
818               fclose(fp);
819             }
820
821           filename = findinrepomd(repo, "deltainfo", &filechksum, &filechksumtype);
822           if (!filename)
823             filename = findinrepomd(repo, "prestodelta", &filechksum, &filechksumtype);
824           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
825             {
826               repo_add_deltainfoxml(repo, fp, 0);
827               fclose(fp);
828             }
829           
830           writecachedrepo(repo, cookie);
831           break;
832
833         case TYPE_SUSETAGS:
834           printf("susetags repo '%s':", cinfo->alias);
835           fflush(stdout);
836           repo = repo_create(pool, cinfo->alias);
837           cinfo->repo = repo;
838           repo->appdata = cinfo;
839           repo->priority = 99 - cinfo->priority;
840           descrdir = 0;
841           defvendor = 0;
842           if ((fp = curlfopen(cinfo, "content", 0, 0, 0)) == 0)
843             {
844               printf(" no content file, skipped\n");
845               repo_free(repo, 1);
846               cinfo->repo = 0;
847               break;
848             }
849           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
850           if (usecachedrepo(repo, cookie))
851             {
852               printf(" cached\n");
853               fclose(fp);
854               break;
855             }
856           sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0);
857           if (!sigfp)
858             {
859               printf(" unsigned, skipped\n");
860               fclose(fp);
861               break;
862             }
863           if (sigfp)
864             {
865               if (!sigpool)
866                 sigpool = read_sigs();
867               if (!checksig(sigpool, fp, sigfp))
868                 {
869                   printf(" checksig failed, skipped\n");
870                   fclose(sigfp);
871                   fclose(fp);
872                   break;
873                 }
874               fclose(sigfp);
875             }
876           repo_add_content(repo, fp, 0);
877           fclose(fp);
878           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
879           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
880           if (!descrdir)
881             descrdir = "suse/setup/descr";
882           filename = 0;
883           dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, "packages.gz", SEARCH_STRING);
884           dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
885           if (dataiterator_step(&di))
886             {
887               dataiterator_setpos_parent(&di);
888               filechksum = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
889               filename = "packages.gz";
890             }
891           dataiterator_free(&di);
892           if (!filename)
893             {
894               dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, "packages", SEARCH_STRING);
895               dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
896               if (dataiterator_step(&di))
897                 {
898                   dataiterator_setpos_parent(&di);
899                   filechksum = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
900                   filename = "packages";
901                 }
902               dataiterator_free(&di);
903             }
904           if (!filename)
905             {
906               printf(" no packages file entry, skipped\n");
907               break;
908             }
909           if (!filechksumtype)
910             {
911               printf(" no packages file checksum, skipped\n");
912               break;
913             }
914           printf(" reading\n");
915           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
916             break;
917           repo_add_susetags(repo, fp, defvendor, 0, 0);
918           fclose(fp);
919           writecachedrepo(repo, cookie);
920           break;
921         default:
922           printf("unsupported repo '%s': skipped\n", cinfo->alias);
923           repo_free(repo, 1);
924           cinfo->repo = 0;
925           break;
926         }
927     }
928   if (sigpool)
929     pool_free(sigpool);
930 }
931
932 void
933 mkselect(Pool *pool, const char *arg, int flags, Queue *out)
934 {
935   Id id, p, pp;
936   Id type = 0;
937   const char *r, *r2;
938
939   id = str2id(pool, arg, 0);
940   if (id)
941     {
942       FOR_PROVIDES(p, pp, id)
943         {
944           Solvable *s = pool_id2solvable(pool, p);
945           if (s->name == id)
946             {
947               type = SOLVER_SOLVABLE_NAME;
948               break;
949             }
950           type = SOLVER_SOLVABLE_PROVIDES;
951         }
952     }
953   if (!type)
954     {
955       /* did not find a solvable, see if it's a relation */
956       if ((r = strpbrk(arg, "<=>")) != 0)
957         {
958           Id rid, rname, revr;
959           int rflags = 0;
960           for (r2 = r; r2 > arg && (r2[-1] == ' ' || r2[-1] == '\t'); )
961             r2--;
962           rname = r2 > arg ? strn2id(pool, arg, r2 - arg, 1) : 0;
963           for (; *r; r++)
964             {
965               if (*r == '<')
966                 rflags |= REL_LT;
967               else if (*r == '=')
968                 rflags |= REL_EQ;
969               else if (*r == '>')
970                 rflags |= REL_GT;
971               else
972                 break;
973             }
974           while (*r == ' ' || *r == '\t')
975             r++;
976           revr = *r ? str2id(pool, r, 1) : 0;
977           rid = rname && revr ? rel2id(pool, rname, revr, rflags, 1) : 0;
978           if (rid)
979             {
980               FOR_PROVIDES(p, pp, rid)
981                 {
982                   Solvable *s = pool_id2solvable(pool, p);
983                   if (pool_match_nevr(pool, s, rid))
984                     {
985                       type = SOLVER_SOLVABLE_NAME;
986                       break;
987                     }
988                   type = SOLVER_SOLVABLE_PROVIDES;
989                 }
990             }
991           if (type)
992             id = rid;
993         }
994     }
995   if (type)
996     {
997       queue_push(out, type);
998       queue_push(out, id);
999     }
1000 }
1001
1002 int
1003 yesno(const char *str)
1004 {
1005   char inbuf[128], *ip;
1006
1007   for (;;)
1008     {
1009       printf("%s", str);
1010       fflush(stdout);
1011       *inbuf = 0;
1012       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1013         {
1014           printf("Abort.\n");
1015           exit(1);
1016         }
1017       while (*ip == ' ' || *ip == '\t')
1018         ip++;
1019       if (*ip == 'q')
1020         {
1021           printf("Abort.\n");
1022           exit(1);
1023         }
1024       if (*ip == 'y' || *ip == 'n')
1025         return *ip == 'y' ? 1 : 0;
1026     }
1027 }
1028
1029 struct fcstate {
1030   FILE **newpkgsfps;
1031   Queue *checkq;
1032   int newpkgscnt;
1033   void *rpmdbstate;
1034 };
1035
1036 static void *
1037 fc_cb(Pool *pool, Id p, void *cbdata)
1038 {
1039   struct fcstate *fcstate = cbdata;
1040   Solvable *s;
1041   Id rpmdbid;
1042   int i;
1043   FILE *fp;
1044
1045   if (!p)
1046     {
1047       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1048       return 0;
1049     }
1050   s = pool_id2solvable(pool, p);
1051   if (pool->installed && s->repo == pool->installed)
1052     {
1053       if (!s->repo->rpmdbid)
1054         return 0;
1055       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1056       if (!rpmdbid)
1057         return 0;
1058        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1059     }
1060   for (i = 0; i < fcstate->newpkgscnt; i++)
1061     if (fcstate->checkq->elements[i] == p)
1062       break;
1063   if (i == fcstate->newpkgscnt)
1064     return 0;
1065   fp = fcstate->newpkgsfps[i];
1066   if (!fp)
1067     return 0;
1068   rewind(fp);
1069   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1070 }
1071
1072 void
1073 runrpm(const char *arg, const char *name, int dupfd3)
1074 {
1075   pid_t pid;
1076   int status;
1077
1078   if ((pid = fork()) == (pid_t)-1)
1079     {
1080       perror("fork");
1081       exit(1);
1082     }
1083   if (pid == 0)
1084     {
1085       if (dupfd3 != -1 && dupfd3 != 3)
1086         {
1087           dup2(dupfd3, 3);
1088           close(dupfd3);
1089         }
1090       if (dupfd3 != -1)
1091         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1092       if (strcmp(arg, "-e") == 0)
1093         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1094       else
1095         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1096       perror("rpm");
1097       _exit(0);
1098     }
1099   while (waitpid(pid, &status, 0) != pid)
1100     ;
1101   if (status)
1102     {
1103       printf("rpm failed\n");
1104       exit(1);
1105     }
1106 }
1107
1108 static Id
1109 nscallback(Pool *pool, void *data, Id name, Id evr)
1110 {
1111   if (name == NAMESPACE_PRODUCTBUDDY)
1112     {    
1113       /* SUSE specific hack: each product has an associated rpm */
1114       Solvable *s = pool->solvables + evr; 
1115       Id p, pp, cap; 
1116       
1117       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1118       if (!cap)
1119         return 0;
1120       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1121       if (!cap)
1122         return 0;
1123       FOR_PROVIDES(p, pp, cap) 
1124         {
1125           Solvable *ps = pool->solvables + p; 
1126           if (ps->repo == s->repo && ps->arch == s->arch)
1127             break;
1128         }
1129       return p;
1130     }
1131   return 0;
1132 }
1133
1134
1135 int
1136 main(int argc, char **argv)
1137 {
1138   Pool *pool;
1139   Id p, pp;
1140   struct repoinfo *repoinfos;
1141   int nrepoinfos = 0;
1142   int i, mode, newpkgs;
1143   Queue job, checkq;
1144   Solver *solv = 0;
1145   Transaction *trans;
1146   char inbuf[128], *ip;
1147   int updateall = 0;
1148   int distupgrade = 0;
1149   int patchmode = 0;
1150   FILE **newpkgsfps;
1151   struct fcstate fcstate;
1152
1153   argc--;
1154   argv++;
1155   if (!argv[0])
1156     {
1157       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1158       exit(1);
1159     }
1160   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
1161     mode = SOLVER_INSTALL;
1162   else if (!strcmp(argv[0], "patch"))
1163     {
1164       mode = SOLVER_UPDATE;
1165       patchmode = 1;
1166     }
1167   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
1168     mode = SOLVER_ERASE;
1169   else if (!strcmp(argv[0], "show"))
1170     mode = 0;
1171   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
1172     mode = SOLVER_UPDATE;
1173   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
1174     {
1175       mode = SOLVER_UPDATE;
1176       distupgrade = 1;
1177     }
1178   else
1179     {
1180       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1181       exit(1);
1182     }
1183
1184   pool = pool_create();
1185   pool->nscallback = nscallback;
1186   // pool_setdebuglevel(pool, 2);
1187   setarch(pool);
1188   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
1189   read_repos(pool, repoinfos, nrepoinfos);
1190   // FOR_REPOS(i, repo)
1191   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
1192   pool_addfileprovides(pool);
1193   pool_createwhatprovides(pool);
1194
1195   queue_init(&job);
1196   for (i = 1; i < argc; i++)
1197     mkselect(pool, argv[i], 0, &job);
1198   if (!job.count && mode == SOLVER_UPDATE)
1199     updateall = 1;
1200   else if (!job.count)
1201     {
1202       printf("no package matched\n");
1203       exit(1);
1204     }
1205
1206   if (!mode)
1207     {
1208       /* show mode, no solver needed */
1209       for (i = 0; i < job.count; i += 2)
1210         {
1211           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1212             {
1213               Solvable *s = pool_id2solvable(pool, p);
1214               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
1215             }
1216         }
1217       exit(0);
1218     }
1219
1220   if (updateall && patchmode)
1221     {
1222       int pruneyou = 0;
1223       Map installedmap;
1224       Solvable *s;
1225
1226       map_init(&installedmap, pool->nsolvables);
1227       if (pool->installed)
1228         FOR_REPO_SOLVABLES(pool->installed, p, s)
1229           MAPSET(&installedmap, p);
1230
1231       /* install all patches */
1232       updateall = 0;
1233       mode = SOLVER_INSTALL;
1234       for (p = 1; p < pool->nsolvables; p++)
1235         {
1236           const char *type;
1237           int r;
1238           Id p2;
1239
1240           s = pool->solvables + p;
1241           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
1242             continue;
1243           FOR_PROVIDES(p2, pp, s->name)
1244             {
1245               Solvable *s2 = pool->solvables + p2;
1246               if (s2->name != s->name)
1247                 continue;
1248               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
1249               if (r < 0 || (r == 0 && p > p2))
1250                 break;
1251             }
1252           if (p2)
1253             continue;
1254           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
1255           if (type && !strcmp(type, "optional"))
1256             continue;
1257           r = solvable_trivial_installable_map(s, &installedmap, 0);
1258           if (r == -1)
1259             continue;
1260           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
1261             {
1262               if (!pruneyou++)
1263                 queue_empty(&job);
1264             }
1265           else if (pruneyou)
1266             continue;
1267           queue_push2(&job, SOLVER_SOLVABLE, p);
1268         }
1269       map_free(&installedmap);
1270     }
1271
1272   // add mode
1273   for (i = 0; i < job.count; i += 2)
1274     job.elements[i] |= mode;
1275
1276   // multiversion test
1277   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
1278   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
1279   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
1280
1281 rerunsolver:
1282   for (;;)
1283     {
1284       Id problem, solution;
1285       int pcnt, scnt;
1286
1287       solv = solver_create(pool);
1288       solv->ignorealreadyrecommended = 1;
1289       solv->updatesystem = updateall;
1290       solv->dosplitprovides = updateall;
1291       if (updateall && distupgrade)
1292         {
1293           solv->distupgrade = 1;
1294           solv->allowdowngrade = 1;
1295           solv->allowarchchange = 1;
1296           solv->allowvendorchange = 1;
1297         }
1298       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
1299       solver_solve(solv, &job);
1300       if (!solv->problems.count)
1301         break;
1302       pcnt = solver_problem_count(solv);
1303       printf("Found %d problems:\n", pcnt);
1304       for (problem = 1; problem <= pcnt; problem++)
1305         {
1306           int take = 0;
1307           printf("Problem %d:\n", problem);
1308           solver_printprobleminfo(solv, problem);
1309           printf("\n");
1310           scnt = solver_solution_count(solv, problem);
1311           for (solution = 1; solution <= scnt; solution++)
1312             {
1313               printf("Solution %d:\n", solution);
1314               solver_printsolution(solv, problem, solution);
1315               printf("\n");
1316             }
1317           for (;;)
1318             {
1319               printf("Please choose a solution: ");
1320               fflush(stdout);
1321               *inbuf = 0;
1322               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1323                 {
1324                   printf("Abort.\n");
1325                   exit(1);
1326                 }
1327               while (*ip == ' ' || *ip == '\t')
1328                 ip++;
1329               if (*ip >= '0' && *ip <= '9')
1330                 {
1331                   take = atoi(ip);
1332                   if (take >= 1 && take <= scnt)
1333                     break;
1334                 }
1335               if (*ip == 's')
1336                 {
1337                   take = 0;
1338                   break;
1339                 }
1340               if (*ip == 'q')
1341                 {
1342                   printf("Abort.\n");
1343                   exit(1);
1344                 }
1345             }
1346           if (!take)
1347             continue;
1348           solver_take_solution(solv, problem, take, &job);
1349         }
1350       solver_free(solv);
1351       solv = 0;
1352     }
1353   if (!solv->trans.steps.count)
1354     {
1355       printf("Nothing to do.\n");
1356       exit(1);
1357     }
1358   printf("\n");
1359   printf("Transaction summary:\n\n");
1360   solver_printtransaction(solv);
1361   if (!yesno("OK to continue (y/n)? "))
1362     {
1363       printf("Abort.\n");
1364       exit(1);
1365     }
1366
1367   trans = &solv->trans;
1368   queue_init(&checkq);
1369   newpkgs = transaction_installedresult(trans, &checkq);
1370   newpkgsfps = 0;
1371
1372   if (newpkgs)
1373     {
1374       printf("Downloading %d packages\n", newpkgs);
1375       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
1376       for (i = 0; i < newpkgs; i++)
1377         {
1378           unsigned int medianr;
1379           char *loc;
1380           Solvable *s;
1381           struct repoinfo *cinfo;
1382           const unsigned char *chksum;
1383           Id chksumtype;
1384           Dataiterator di;
1385
1386           p = checkq.elements[i];
1387           s = pool_id2solvable(pool, p);
1388           cinfo = s->repo->appdata;
1389           if (!cinfo)
1390             {
1391               printf("%s: no repository information\n", s->repo->name);
1392               exit(1);
1393             }
1394           loc = solvable_get_location(s, &medianr);
1395           if (!loc)
1396              continue;
1397
1398           if (pool->installed && pool->installed->nsolvables)
1399             {
1400               /* try a delta first */
1401               dataiterator_init(&di, pool, 0, SOLVID_META, DELTA_PACKAGE_NAME, id2str(pool, s->name), SEARCH_STRING);
1402               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
1403               while (dataiterator_step(&di))
1404                 {
1405                   Id baseevr, op;
1406
1407                   dataiterator_setpos_parent(&di);
1408                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
1409                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
1410                     continue;
1411                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
1412                   FOR_PROVIDES(op, pp, s->name)
1413                     {
1414                       Solvable *os = pool->solvables + op;
1415                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
1416                         break;
1417                     }
1418                   if (op)
1419                     {
1420                       /* base is installed, run sequence check */
1421                       const char *seqname;
1422                       const char *seqevr;
1423                       const char *seqnum;
1424                       const char *seq;
1425                       const char *dloc;
1426                       FILE *fp;
1427                       char cmd[128];
1428                       int newfd;
1429
1430                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
1431                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
1432                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
1433                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
1434                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
1435                       if (system(pool_tmpjoin(pool, "applydeltarpm -c -s ", seq, 0)) != 0)
1436                         continue;       /* didn't match */
1437                       /* looks good, download delta */
1438                       chksumtype = 0;
1439                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
1440                       if (!chksumtype)
1441                         continue;       /* no way! */
1442                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
1443                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
1444                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
1445                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
1446                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype)) == 0)
1447                         continue;
1448                       /* got it, now reconstruct */
1449                       newfd = opentmpfile();
1450                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
1451                       fcntl(fileno(fp), F_SETFD, 0);
1452                       if (system(cmd))
1453                         {
1454                           close(newfd);
1455                           fclose(fp);
1456                           continue;
1457                         }
1458                       lseek(newfd, 0, SEEK_SET);
1459                       chksumtype = 0;
1460                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
1461                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
1462                         {
1463                           close(newfd);
1464                           fclose(fp);
1465                           continue;
1466                         }
1467                       newpkgsfps[i] = fdopen(newfd, "r");
1468                       fclose(fp);
1469                       break;
1470                     }
1471                 }
1472               dataiterator_free(&di);
1473             }
1474           
1475           if (newpkgsfps[i])
1476             {
1477               putchar('d');
1478               fflush(stdout);
1479               continue;         /* delta worked! */
1480             }
1481           if (cinfo->type == TYPE_SUSETAGS)
1482             {
1483               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
1484               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
1485             }
1486           chksumtype = 0;
1487           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
1488           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype)) == 0)
1489             {
1490               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
1491               exit(1);
1492             }
1493           putchar('.');
1494           fflush(stdout);
1495         }
1496       putchar('\n');
1497     }
1498
1499   if (newpkgs)
1500     {
1501       Queue conflicts;
1502
1503       printf("Searching for file conflicts\n");
1504       queue_init(&conflicts);
1505       fcstate.rpmdbstate = 0;
1506       fcstate.newpkgscnt = newpkgs;
1507       fcstate.checkq = &checkq;
1508       fcstate.newpkgsfps = newpkgsfps;
1509       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
1510       if (conflicts.count)
1511         {
1512           printf("\n");
1513           for (i = 0; i < conflicts.count; i += 5)
1514             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]));
1515           printf("\n");
1516           if (yesno("Re-run solver (y/n/q)? "))
1517             {
1518               for (i = 0; i < newpkgs; i++)
1519                 if (newpkgsfps[i])
1520                   fclose(newpkgsfps[i]);
1521               newpkgsfps = sat_free(newpkgsfps);
1522               solver_free(solv);
1523               pool_add_fileconflicts_deps(pool, &conflicts);
1524               pool_createwhatprovides(pool);    /* Hmm... */
1525               goto rerunsolver;
1526             }
1527         }
1528       queue_free(&conflicts);
1529     }
1530
1531   printf("Committing transaction:\n\n");
1532   transaction_order(trans, 0);
1533   for (i = 0; i < trans->steps.count; i++)
1534     {
1535       const char *evr, *evrp, *nvra;
1536       Solvable *s;
1537       int j;
1538       FILE *fp;
1539
1540       p = trans->steps.elements[i];
1541       s = pool_id2solvable(pool, p);
1542       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
1543       switch(type)
1544         {
1545         case SOLVER_TRANSACTION_ERASE:
1546           printf("erase %s\n", solvid2str(pool, p));
1547           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
1548             continue;
1549           /* strip epoch from evr */
1550           evr = evrp = id2str(pool, s->evr);
1551           while (*evrp >= '0' && *evrp <= '9')
1552             evrp++;
1553           if (evrp > evr && evrp[0] == ':' && evrp[1])
1554             evr = evrp + 1;
1555           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
1556           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
1557           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
1558           break;
1559         case SOLVER_TRANSACTION_INSTALL:
1560         case SOLVER_TRANSACTION_MULTIINSTALL:
1561           printf("install %s\n", solvid2str(pool, p));
1562           for (j = 0; j < newpkgs; j++)
1563             if (checkq.elements[j] == p)
1564               break;
1565           fp = j < newpkgs ? newpkgsfps[j] : 0;
1566           if (!fp)
1567             continue;
1568           rewind(fp);
1569           lseek(fileno(fp), 0, SEEK_SET);
1570           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
1571           fclose(fp);
1572           newpkgsfps[j] = 0;
1573           break;
1574         default:
1575           break;
1576         }
1577     }
1578
1579   for (i = 0; i < newpkgs; i++)
1580     if (newpkgsfps[i])
1581       fclose(newpkgsfps[i]);
1582   sat_free(newpkgsfps);
1583   queue_free(&checkq);
1584   solver_free(solv);
1585   queue_free(&job);
1586   pool_free(pool);
1587   exit(0);
1588 }