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