- fix typos
[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  * - repository data caching
12  * - signature verification
13  */
14
15 #define _GNU_SOURCE
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <dirent.h>
20 #include <unistd.h>
21 #include <zlib.h>
22 #include <fcntl.h>
23 #include <sys/utsname.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26
27 #include "pool.h"
28 #include "poolarch.h"
29 #include "repo.h"
30 #include "util.h"
31 #include "solver.h"
32 #include "solverdebug.h"
33
34 #include "repo_rpmdb.h"
35 #include "repo_products.h"
36 #include "repo_rpmmd.h"
37 #include "repo_susetags.h"
38 #include "repo_repomdxml.h"
39 #include "repo_content.h"
40 #include "pool_fileconflicts.h"
41
42
43 #define ZYPP_REPOINFO_PATH "/etc/zypp/repos.d"
44 #define ZYPP_PRODUCTS_PATH "/etc/products.d"
45
46 struct repoinfo {
47   Repo *repo;
48
49   char *alias;
50   char *name;
51   int enabled;
52   int autorefresh;
53   char *baseurl;
54   char *path;
55   int type;
56   int priority;
57   int keeppackages;
58 };
59
60 #define TYPE_UNKNOWN 0
61 #define TYPE_SUSETAGS 1
62 #define TYPE_RPMMD 2
63
64 struct repoinfo *
65 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
66 {
67   char buf[4096];
68   char buf2[4096], *kp, *vp, *kpe;
69   DIR *dir;
70   FILE *fp;
71   struct dirent *ent;
72   int l, rdlen;
73   struct repoinfo *repoinfos = 0, *cinfo;
74   int nrepoinfos = 0;
75
76   rdlen = strlen(reposdir);
77   dir = opendir(reposdir);
78   if (!dir)
79     {
80       *nrepoinfosp = 0;
81       return 0;
82     }
83   while ((ent = readdir(dir)) != 0)
84     {
85       l = strlen(ent->d_name);
86       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
87         continue;
88       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
89       if ((fp = fopen(buf, "r")) == 0)
90         {
91           perror(buf);
92           continue;
93         }
94       cinfo = 0;
95       while(fgets(buf2, sizeof(buf2), fp))
96         {
97           l = strlen(buf2);
98           if (l == 0)
99             continue;
100           while (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t')
101             buf2[--l] = 0;
102           kp = buf2;
103           while (*kp == ' ' || *kp == '\t')
104             kp++;
105           if (!*kp || *kp == '#')
106             continue;
107           if (!cinfo)
108             {
109               if (*kp != '[')
110                 continue;
111               vp = strrchr(kp, ']');
112               if (!vp)
113                 continue;
114               *vp = 0;
115               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
116               cinfo = repoinfos + nrepoinfos++;
117               memset(cinfo, 0, sizeof(*cinfo));
118               cinfo->alias = strdup(kp + 1);
119               continue;
120             }
121           vp = strchr(kp, '=');
122           if (!vp)
123             continue;
124           for (kpe = vp - 1; kpe >= kp; kpe--)
125             if (*kpe != ' ' && *kpe != '\t')
126               break;
127           if (kpe == kp)
128             continue;
129           vp++;
130           while (*vp == ' ' || *vp == '\t')
131             vp++;
132           kpe[1] = 0;
133           if (!strcmp(kp, "name"))
134             cinfo->name = strdup(vp);
135           else if (!strcmp(kp, "enabled"))
136             cinfo->enabled = *vp == '0' ? 0 : 1;
137           else if (!strcmp(kp, "autorefresh"))
138             cinfo->autorefresh = *vp == '0' ? 0 : 1;
139           else if (!strcmp(kp, "baseurl"))
140             cinfo->baseurl = strdup(vp);
141           else if (!strcmp(kp, "path"))
142             cinfo->path = strdup(vp);
143           else if (!strcmp(kp, "type"))
144             {
145               if (!strcmp(vp, "yast2"))
146                 cinfo->type = TYPE_SUSETAGS;
147               else if (!strcmp(vp, "rpm-md"))
148                 cinfo->type = TYPE_RPMMD;
149               else
150                 cinfo->type = TYPE_UNKNOWN;
151             }
152           else if (!strcmp(kp, "priority"))
153             cinfo->priority = atoi(vp);
154           else if (!strcmp(kp, "keeppackages"))
155             cinfo->keeppackages = *vp == '0' ? 0 : 1;
156         }
157       fclose(fp);
158       cinfo = 0;
159     }
160   closedir(dir);
161   *nrepoinfosp = nrepoinfos;
162   return repoinfos;
163 }
164
165 static ssize_t
166 cookie_gzread(void *cookie, char *buf, size_t nbytes)
167 {
168   return gzread((gzFile *)cookie, buf, nbytes);
169 }
170
171 static int
172 cookie_gzclose(void *cookie)
173 {
174   return gzclose((gzFile *)cookie);
175 }
176
177 FILE *
178 myfopen(const char *fn)
179 {
180   cookie_io_functions_t cio;
181   char *suf;
182   gzFile *gzf;
183
184   if (!fn)
185     return 0;
186   suf = strrchr(fn, '.');
187   if (!suf || strcmp(suf, ".gz") != 0)
188     return fopen(fn, "r");
189   gzf = gzopen(fn, "r");
190   if (!gzf)
191     return 0;
192   memset(&cio, 0, sizeof(cio));
193   cio.read = cookie_gzread;
194   cio.close = cookie_gzclose;
195   return  fopencookie(gzf, "r", cio);
196 }
197
198 FILE *
199 curlfopen(const char *baseurl, const char *file, int uncompress)
200 {
201   pid_t pid;
202   int fd, l;
203   int status;
204   char tmpl[100];
205   char url[4096];
206
207   l = strlen(baseurl);
208   if (l && baseurl[l - 1] == '/')
209     snprintf(url, sizeof(url), "%s%s", baseurl, file);
210   else
211     snprintf(url, sizeof(url), "%s/%s", baseurl, file);
212   strcpy(tmpl, "/var/tmp/solvXXXXXX");
213   fd = mkstemp(tmpl);
214   if (fd < 0)
215     {
216       perror("mkstemp");
217       exit(1);
218     }
219   unlink(tmpl);
220   if ((pid = fork()) == (pid_t)-1)
221     {
222       perror("fork");
223       exit(1);
224     }
225   if (pid == 0)
226     {
227       if (fd != 1)
228         {
229           dup2(fd, 1);
230           close(fd);
231         }
232       execlp("curl", "curl", "-s", "-L", url, (char *)0);
233       perror("curl");
234       _exit(0);
235     }
236   while (waitpid(pid, &status, 0) != pid)
237     ;
238   if (lseek(fd, 0, SEEK_END) == 0)
239     {
240       /* empty file */
241       close(fd);
242       return 0;
243     }
244   lseek(fd, 0, SEEK_SET);
245   if (uncompress)
246     {
247       cookie_io_functions_t cio;
248       gzFile *gzf;
249
250       sprintf(tmpl, "/dev/fd/%d", fd);
251       gzf = gzopen(tmpl, "r");
252       close(fd);
253       if (!gzf)
254         return 0;
255       memset(&cio, 0, sizeof(cio));
256       cio.read = cookie_gzread;
257       cio.close = cookie_gzclose;
258       return fopencookie(gzf, "r", cio);
259     }
260   fcntl(fd, F_SETFD, FD_CLOEXEC);
261   return fdopen(fd, "r");
262 }
263
264 void
265 setarch(Pool *pool)
266 {
267   struct utsname un;
268   if (uname(&un))
269     {
270       perror("uname");
271       exit(1);
272     }
273   pool_setarch(pool, un.machine);
274 }
275
276
277 void
278 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
279 {
280   Repo *repo;
281   struct repoinfo *cinfo;
282   int i;
283   FILE *fp;
284   Dataiterator di;
285   const char *primaryfile;
286   const char *descrdir;
287   int defvendor;
288
289   printf("reading rpm database\n");
290   repo = repo_create(pool, "@System");
291   repo_add_products(repo, ZYPP_PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
292   repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
293   
294   pool_set_installed(pool, repo);
295   for (i = 0; i < nrepoinfos; i++)
296     {
297       cinfo = repoinfos + i;
298       if (!cinfo->enabled)
299         continue;
300       switch (cinfo->type)
301         {
302         case TYPE_RPMMD:
303           printf("reading rpmmd repo '%s'\n", cinfo->alias);
304           if ((fp = curlfopen(cinfo->baseurl, "repodata/repomd.xml", 0)) == 0)
305             break;
306           repo = repo_create(pool, cinfo->alias);
307           cinfo->repo = repo;
308           repo->appdata = cinfo;
309           repo->priority = 99 - cinfo->priority;
310           repo_add_repomdxml(repo, fp, 0);
311           fclose(fp);
312           primaryfile = 0;
313           dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, "primary", SEARCH_STRING);
314           dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
315           if (dataiterator_step(&di))
316             {
317               dataiterator_setpos_parent(&di);
318               primaryfile = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
319             }
320           dataiterator_free(&di);
321           if (!primaryfile)
322             primaryfile = "repodata/primary.xml.gz";
323           if ((fp = curlfopen(cinfo->baseurl, primaryfile, 1)) == 0)
324             continue;
325           repo_add_rpmmd(repo, fp, 0, 0);
326           fclose(fp);
327           break;
328         case TYPE_SUSETAGS:
329           printf("reading susetags repo '%s'\n", cinfo->alias);
330           repo = repo_create(pool, cinfo->alias);
331           cinfo->repo = repo;
332           repo->appdata = cinfo;
333           repo->priority = 99 - cinfo->priority;
334           descrdir = 0;
335           defvendor = 0;
336           if ((fp = curlfopen(cinfo->baseurl, "content", 0)) != 0)
337             {
338               repo_add_content(repo, fp, 0);
339               fclose(fp);
340               defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
341               descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
342             }
343           if (!descrdir)
344             descrdir = "suse/setup/descr";
345           if ((fp = curlfopen(cinfo->baseurl, pool_tmpjoin(pool, descrdir, "/packages.gz", 0), 1)) == 0)
346             if ((fp = curlfopen(cinfo->baseurl, pool_tmpjoin(pool, descrdir, "/packages", 0), 0)) == 0)
347               break;
348           repo_add_susetags(repo, fp, defvendor, 0, 0);
349           fclose(fp);
350           break;
351         default:
352           printf("skipping unknown repo '%s'\n", cinfo->alias);
353           break;
354         }
355     }
356 }
357
358 void
359 mkselect(Pool *pool, const char *arg, int flags, Queue *out)
360 {
361   Id id, p, pp;
362   Id type = 0;
363   const char *r, *r2;
364
365   id = str2id(pool, arg, 0);
366   if (id)
367     {
368       FOR_PROVIDES(p, pp, id)
369         {
370           Solvable *s = pool_id2solvable(pool, p);
371           if (s->name == id)
372             {
373               type = SOLVER_SOLVABLE_NAME;
374               break;
375             }
376           type = SOLVER_SOLVABLE_PROVIDES;
377         }
378     }
379   if (!type)
380     {
381       /* did not find a solvable, see if it's a relation */
382       if ((r = strpbrk(arg, "<=>")) != 0)
383         {
384           Id rid, rname, revr;
385           int rflags = 0;
386           for (r2 = r; r2 > arg && (r2[-1] == ' ' || r2[-1] == '\t'); )
387             r2--;
388           rname = r2 > arg ? strn2id(pool, arg, r2 - arg, 1) : 0;
389           for (; *r; r++)
390             {
391               if (*r == '<')
392                 rflags |= REL_LT;
393               else if (*r == '=')
394                 rflags |= REL_EQ;
395               else if (*r == '>')
396                 rflags |= REL_GT;
397               else
398                 break;
399             }
400           while (*r == ' ' || *r == '\t')
401             r++;
402           revr = *r ? str2id(pool, r, 1) : 0;
403           rid = rname && revr ? rel2id(pool, rname, revr, rflags, 1) : 0;
404           if (rid)
405             {
406               FOR_PROVIDES(p, pp, rid)
407                 {
408                   Solvable *s = pool_id2solvable(pool, p);
409                   if (pool_match_nevr(pool, s, rid))
410                     {
411                       type = SOLVER_SOLVABLE_NAME;
412                       break;
413                     }
414                   type = SOLVER_SOLVABLE_PROVIDES;
415                 }
416             }
417           if (type)
418             id = rid;
419         }
420     }
421   if (type)
422     {
423       queue_push(out, type);
424       queue_push(out, id);
425     }
426 }
427
428 int
429 yesno(const char *str)
430 {
431   char inbuf[128], *ip;
432
433   for (;;)
434     {
435       printf("%s", str);
436       fflush(stdout);
437       *inbuf = 0;
438       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
439         {
440           printf("Abort.\n");
441           exit(1);
442         }
443       while (*ip == ' ' || *ip == '\t')
444         ip++;
445       if (*ip == 'q')
446         {
447           printf("Abort.\n");
448           exit(1);
449         }
450       if (*ip == 'y' || *ip == 'n')
451         return *ip == 'y' ? 1 : 0;
452     }
453 }
454
455 struct fcstate {
456   FILE **newpkgsfps;
457   Queue *checkq;
458   int newpkgscnt;
459   void *rpmdbstate;
460 };
461
462 static void *
463 fc_cb(Pool *pool, Id p, void *cbdata)
464 {
465   struct fcstate *fcstate = cbdata;
466   Solvable *s;
467   Id rpmdbid;
468   int i;
469   FILE *fp;
470
471   if (!p)
472     {
473       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
474       return 0;
475     }
476   s = pool_id2solvable(pool, p);
477   if (pool->installed && s->repo == pool->installed)
478     {
479       if (!s->repo->rpmdbid)
480         return 0;
481       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
482       if (!rpmdbid)
483         return 0;
484        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
485     }
486   for (i = 0; i < fcstate->newpkgscnt; i++)
487     if (fcstate->checkq->elements[i] == p)
488       break;
489   if (i == fcstate->newpkgscnt)
490     return 0;
491   fp = fcstate->newpkgsfps[i];
492   if (!fp)
493     return 0;
494   rewind(fp);
495   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
496 }
497
498 void
499 runrpm(const char *arg, const char *name, int dupfd3)
500 {
501   pid_t pid;
502   int status;
503
504   if ((pid = fork()) == (pid_t)-1)
505     {
506       perror("fork");
507       exit(1);
508     }
509   if (pid == 0)
510     {
511       if (dupfd3 != -1 && dupfd3 != 3)
512         {
513           dup2(dupfd3, 3);
514           close(dupfd3);
515         }
516       if (dupfd3 != -1)
517         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
518       if (strcmp(arg, "-e") == 0)
519         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
520       else
521         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
522       perror("rpm");
523       _exit(0);
524     }
525   while (waitpid(pid, &status, 0) != pid)
526     ;
527   if (status)
528     {
529       printf("rpm failed\n");
530       exit(1);
531     }
532 }
533
534 static Id
535 nscallback(Pool *pool, void *data, Id name, Id evr)
536 {
537   if (name == NAMESPACE_PRODUCTBUDDY)
538     {    
539       /* SUSE specific hack: each product has an associated rpm */
540       Solvable *s = pool->solvables + evr; 
541       Id p, pp, cap; 
542       
543       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
544       if (!cap)
545         return 0;
546       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
547       if (!cap)
548         return 0;
549       FOR_PROVIDES(p, pp, cap) 
550         {
551           Solvable *ps = pool->solvables + p; 
552           if (ps->repo == s->repo && ps->arch == s->arch)
553             break;
554         }
555       return p;
556     }
557   return 0;
558 }
559
560
561 int
562 main(int argc, char **argv)
563 {
564   Pool *pool;
565   Id p, pp;
566   struct repoinfo *repoinfos;
567   int nrepoinfos = 0;
568   int i, mode, newpkgs;
569   Queue job, checkq;
570   Solver *solv = 0;
571   Transaction *trans;
572   char inbuf[128], *ip;
573   int updateall = 0;
574   FILE **newpkgsfps;
575   struct fcstate fcstate;
576
577   if (!strcmp(argv[1], "install") || !strcmp(argv[1], "in"))
578     mode = SOLVER_INSTALL;
579   else if (!strcmp(argv[1], "erase") || !strcmp(argv[1], "rm"))
580     mode = SOLVER_ERASE;
581   else if (!strcmp(argv[1], "show"))
582     mode = 0;
583   else if (!strcmp(argv[1], "update") || !strcmp(argv[1], "up"))
584     mode = SOLVER_UPDATE;
585   else
586     {
587       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
588       exit(1);
589     }
590
591   pool = pool_create();
592   pool->nscallback = nscallback;
593   // pool_setdebuglevel(pool, 2);
594   setarch(pool);
595   repoinfos = read_repoinfos(pool, ZYPP_REPOINFO_PATH, &nrepoinfos);
596   read_repos(pool, repoinfos, nrepoinfos);
597   // FOR_REPOS(i, repo)
598   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
599   pool_addfileprovides(pool);
600   pool_createwhatprovides(pool);
601
602   queue_init(&job);
603   for (i = 2; i < argc; i++)
604     mkselect(pool, argv[i], 0, &job);
605   if (!job.count && mode == SOLVER_UPDATE)
606     updateall = 1;
607   else if (!job.count)
608     {
609       printf("nothing matched\n");
610       exit(1);
611     }
612
613   if (!mode)
614     {
615       /* show mode, no solver needed */
616       for (i = 0; i < job.count; i += 2)
617         {
618           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
619             {
620               Solvable *s = pool_id2solvable(pool, p);
621               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
622             }
623         }
624       exit(0);
625     }
626
627   // add mode
628   for (i = 0; i < job.count; i += 2)
629     job.elements[i] |= mode;
630
631   // multiversion test
632   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
633   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
634   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
635
636 rerunsolver:
637   for (;;)
638     {
639       Id problem, solution;
640       int pcnt, scnt;
641
642       solv = solver_create(pool);
643       solv->ignorealreadyrecommended = 1;
644       solv->updatesystem = updateall;
645       solver_solve(solv, &job);
646       if (!solv->problems.count)
647         break;
648       pcnt = solver_problem_count(solv);
649       printf("Found %d problems:\n", pcnt);
650       for (problem = 1; problem <= pcnt; problem++)
651         {
652           int take = 0;
653           printf("Problem %d:\n", problem);
654           solver_printprobleminfo(solv, problem);
655           printf("\n");
656           scnt = solver_solution_count(solv, problem);
657           for (solution = 1; solution <= scnt; solution++)
658             {
659               printf("Solution %d:\n", solution);
660               solver_printsolution(solv, problem, solution);
661               printf("\n");
662             }
663           for (;;)
664             {
665               printf("Please choose a solution: ");
666               fflush(stdout);
667               *inbuf = 0;
668               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
669                 {
670                   printf("Abort.\n");
671                   exit(1);
672                 }
673               while (*ip == ' ' || *ip == '\t')
674                 ip++;
675               if (*ip >= '0' && *ip <= '9')
676                 {
677                   take = atoi(ip);
678                   if (take >= 1 && take <= scnt)
679                     break;
680                 }
681               if (*ip == 's')
682                 {
683                   take = 0;
684                   break;
685                 }
686               if (*ip == 'q')
687                 {
688                   printf("Abort.\n");
689                   exit(1);
690                 }
691             }
692           if (!take)
693             continue;
694           solver_take_solution(solv, problem, take, &job);
695         }
696       solver_free(solv);
697       solv = 0;
698     }
699   if (!solv->trans.steps.count)
700     {
701       printf("Nothing to do.\n");
702       exit(1);
703     }
704   printf("\n");
705   printf("Transaction summary:\n\n");
706   solver_printtransaction(solv);
707   if (!yesno("OK to continue (y/n)? "))
708     {
709       printf("Abort.\n");
710       exit(1);
711     }
712
713   trans = &solv->trans;
714   queue_init(&checkq);
715   newpkgs = transaction_installedresult(trans, &checkq);
716   newpkgsfps = 0;
717
718   if (newpkgs)
719     {
720       printf("Downloading %d packages\n", newpkgs);
721       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
722       for (i = 0; i < newpkgs; i++)
723         {
724           unsigned int medianr;
725           char *loc;
726           Solvable *s;
727           struct repoinfo *cinfo;
728
729           p = checkq.elements[i];
730           s = pool_id2solvable(pool, p);
731           cinfo = s->repo->appdata;
732           if (!cinfo)
733             {
734               printf("%s: no repository information\n", s->repo->name);
735               exit(1);
736             }
737           loc = solvable_get_location(s, &medianr);
738           if (!loc)
739              continue;
740           if (cinfo->type == TYPE_SUSETAGS)
741             {
742               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
743               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
744             }
745           if ((newpkgsfps[i] = curlfopen(cinfo->baseurl, loc, 0)) == 0)
746             {
747               printf("%s: %s not found in repository\n", s->repo->name, loc);
748               exit(1);
749             }
750         }
751     }
752
753   if (newpkgs)
754     {
755       Queue conflicts;
756
757       printf("Searching for file conflicts\n");
758       queue_init(&conflicts);
759       fcstate.rpmdbstate = 0;
760       fcstate.newpkgscnt = newpkgs;
761       fcstate.checkq = &checkq;
762       fcstate.newpkgsfps = newpkgsfps;
763       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
764       if (conflicts.count)
765         {
766           printf("\n");
767           for (i = 0; i < conflicts.count; i += 5)
768             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]));
769           printf("\n");
770           if (yesno("Re-run solver (y/n/q)? "))
771             {
772               for (i = 0; i < newpkgs; i++)
773                 if (newpkgsfps[i])
774                   fclose(newpkgsfps[i]);
775               newpkgsfps = sat_free(newpkgsfps);
776               solver_free(solv);
777               pool_add_fileconflicts_deps(pool, &conflicts);
778               pool_createwhatprovides(pool);    /* Hmm... */
779               goto rerunsolver;
780             }
781         }
782       queue_free(&conflicts);
783     }
784
785   printf("Committing transaction:\n\n");
786   transaction_order(trans, 0);
787   for (i = 0; i < trans->steps.count; i++)
788     {
789       char rpmname[256];
790       const char *evr, *evrp;
791       Solvable *s;
792       int j;
793       FILE *fp;
794
795       p = trans->steps.elements[i];
796       s = pool_id2solvable(pool, p);
797       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
798       switch(type)
799         {
800         case SOLVER_TRANSACTION_ERASE:
801           printf("erase %s\n", solvid2str(pool, p));
802           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
803             continue;
804           if (strlen(solvid2str(pool, p)) > 255)
805             continue;
806           evr = evrp = id2str(pool, s->evr);
807           while (*evrp >= '0' && *evrp <= '9')
808             evrp++;
809           if (evrp > evr && evrp[0] == ':' && evrp[1])
810             evr = evrp + 1;
811           sprintf(rpmname, "%s-%s.%s", id2str(pool, s->name), evr, id2str(pool, s->arch));
812           runrpm("-e", rpmname, -1);
813           break;
814         case SOLVER_TRANSACTION_INSTALL:
815         case SOLVER_TRANSACTION_MULTIINSTALL:
816           printf("install %s\n", solvid2str(pool, p));
817           for (j = 0; j < newpkgs; j++)
818             if (checkq.elements[j] == p)
819               break;
820           fp = j < newpkgs ? newpkgsfps[j] : 0;
821           if (!fp)
822             continue;
823           rewind(fp);
824           lseek(fileno(fp), 0, SEEK_SET);
825           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
826           fclose(fp);
827           newpkgsfps[j] = 0;
828           break;
829         default:
830           break;
831         }
832     }
833
834   for (i = 0; i < newpkgs; i++)
835     if (newpkgsfps[i])
836       fclose(newpkgsfps[i]);
837   sat_free(newpkgsfps);
838   queue_free(&checkq);
839   solver_free(solv);
840   queue_free(&job);
841   pool_free(pool);
842   exit(0);
843 }