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