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