- use descrdir and datadir info from content file
[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_add_repomdxml(repo, fp, 0);
310           fclose(fp);
311           primaryfile = 0;
312           dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, "primary", SEARCH_STRING);
313           dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
314           if (dataiterator_step(&di))
315             {
316               dataiterator_setpos_parent(&di);
317               primaryfile = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
318             }
319           dataiterator_free(&di);
320           if (!primaryfile)
321             primaryfile = "repodata/primary.xml.gz";
322           if ((fp = curlfopen(cinfo->baseurl, primaryfile, 1)) == 0)
323             continue;
324           repo_add_rpmmd(repo, fp, 0, 0);
325           fclose(fp);
326           break;
327         case TYPE_SUSETAGS:
328           printf("reading susetags repo '%s'\n", cinfo->alias);
329           repo = repo_create(pool, cinfo->alias);
330           cinfo->repo = repo;
331           repo->appdata = cinfo;
332           descrdir = 0;
333           defvendor = 0;
334           if ((fp = curlfopen(cinfo->baseurl, "content", 0)) != 0)
335             {
336               repo_add_content(repo, fp, 0);
337               fclose(fp);
338               defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
339               descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
340             }
341           if (!descrdir)
342             descrdir = "suse/setup/descr";
343           if ((fp = curlfopen(cinfo->baseurl, pool_tmpjoin(pool, descrdir, "/packages.gz", 0), 1)) == 0)
344             if ((fp = curlfopen(cinfo->baseurl, pool_tmpjoin(pool, descrdir, "/packages", 0), 0)) == 0)
345               break;
346           repo_add_susetags(repo, fp, defvendor, 0, 0);
347           fclose(fp);
348           break;
349         default:
350           printf("skipping unknown repo '%s'\n", cinfo->alias);
351           break;
352         }
353     }
354 }
355
356 void
357 mkselect(Pool *pool, const char *arg, int flags, Queue *out)
358 {
359   Id id, p, pp;
360   Id type = 0;
361   const char *r, *r2;
362
363   id = str2id(pool, arg, 0);
364   if (id)
365     {
366       FOR_PROVIDES(p, pp, id)
367         {
368           Solvable *s = pool_id2solvable(pool, p);
369           if (s->name == id)
370             {
371               type = SOLVER_SOLVABLE_NAME;
372               break;
373             }
374           type = SOLVER_SOLVABLE_PROVIDES;
375         }
376     }
377   if (!type)
378     {
379       /* did not find a solvable, see if it's a relation */
380       if ((r = strpbrk(arg, "<=>")) != 0)
381         {
382           Id rid, rname, revr;
383           int rflags = 0;
384           for (r2 = r; r2 > arg && (r2[-1] == ' ' || r2[-1] == '\t'); )
385             r2--;
386           rname = r2 > arg ? strn2id(pool, arg, r2 - arg, 1) : 0;
387           for (; *r; r++)
388             {
389               if (*r == '<')
390                 rflags |= REL_LT;
391               else if (*r == '=')
392                 rflags |= REL_EQ;
393               else if (*r == '>')
394                 rflags |= REL_GT;
395               else
396                 break;
397             }
398           while (*r == ' ' || *r == '\t')
399             r++;
400           revr = *r ? str2id(pool, r, 1) : 0;
401           rid = rname && revr ? rel2id(pool, rname, revr, rflags, 1) : 0;
402           if (rid)
403             {
404               FOR_PROVIDES(p, pp, rid)
405                 {
406                   Solvable *s = pool_id2solvable(pool, p);
407                   if (pool_match_nevr(pool, s, rid))
408                     {
409                       type = SOLVER_SOLVABLE_NAME;
410                       break;
411                     }
412                   type = SOLVER_SOLVABLE_PROVIDES;
413                 }
414             }
415           if (type)
416             id = rid;
417         }
418     }
419   if (type)
420     {
421       queue_push(out, type);
422       queue_push(out, id);
423     }
424 }
425
426 int
427 yesno(const char *str)
428 {
429   char inbuf[128], *ip;
430
431   for (;;)
432     {
433       printf("%s", str);
434       fflush(stdout);
435       *inbuf = 0;
436       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
437         {
438           printf("Abort.\n");
439           exit(1);
440         }
441       while (*ip == ' ' || *ip == '\t')
442         ip++;
443       if (*ip == 'q')
444         {
445           printf("Abort.\n");
446           exit(1);
447         }
448       if (*ip == 'y' || *ip == 'n')
449         return *ip == 'y' ? 1 : 0;
450     }
451 }
452
453 struct fcstate {
454   FILE **newpkgsfps;
455   Queue *checkq;
456   int newpkgscnt;
457   void *rpmdbstate;
458 };
459
460 static void *
461 fc_cb(Pool *pool, Id p, void *cbdata)
462 {
463   struct fcstate *fcstate = cbdata;
464   Solvable *s;
465   Id rpmdbid;
466   int i;
467   FILE *fp;
468
469   if (!p)
470     {
471       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
472       return 0;
473     }
474   s = pool_id2solvable(pool, p);
475   if (pool->installed && s->repo == pool->installed)
476     {
477       if (!s->repo->rpmdbid)
478         return 0;
479       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
480       if (!rpmdbid)
481         return 0;
482        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
483     }
484   for (i = 0; i < fcstate->newpkgscnt; i++)
485     if (fcstate->checkq->elements[i] == p)
486       break;
487   if (i == fcstate->newpkgscnt)
488     return 0;
489   fp = fcstate->newpkgsfps[i];
490   rewind(fp);
491   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
492 }
493
494 void
495 runrpm(const char *arg, const char *name, int dupfd3)
496 {
497   pid_t pid;
498   int status;
499
500   if ((pid = fork()) == (pid_t)-1)
501     {
502       perror("fork");
503       exit(1);
504     }
505   if (pid == 0)
506     {
507       if (dupfd3 != -1 && dupfd3 != 3)
508         {
509           dup2(dupfd3, 3);
510           close(dupfd3);
511         }
512       if (dupfd3 != -1)
513         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
514       if (strcmp(arg, "-e") == 0)
515         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
516       else
517         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
518       perror("rpm");
519       _exit(0);
520     }
521   while (waitpid(pid, &status, 0) != pid)
522     ;
523   if (status)
524     {
525       printf("rpm failed\n");
526       exit(1);
527     }
528 }
529
530 int
531 main(int argc, char **argv)
532 {
533   Pool *pool;
534   Id p, pp;
535   struct repoinfo *repoinfos;
536   int nrepoinfos = 0;
537   int i, mode, newpkgs;
538   Queue job, checkq;
539   Solver *solv = 0;
540   Transaction *trans;
541   char inbuf[128], *ip;
542   int updateall = 0;
543   FILE **newpkgsfps;
544   struct fcstate fcstate;
545
546   if (!strcmp(argv[1], "install") || !strcmp(argv[1], "in"))
547     mode = SOLVER_INSTALL;
548   else if (!strcmp(argv[1], "erase") || !strcmp(argv[1], "rm"))
549     mode = SOLVER_ERASE;
550   else if (!strcmp(argv[1], "show"))
551     mode = 0;
552   else if (!strcmp(argv[1], "update") || !strcmp(argv[1], "up"))
553     mode = SOLVER_UPDATE;
554   else
555     {
556       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
557       exit(1);
558     }
559
560   pool = pool_create();
561   // pool_setdebuglevel(pool, 2);
562   setarch(pool);
563   repoinfos = read_repoinfos(pool, ZYPP_REPOINFO_PATH, &nrepoinfos);
564   read_repos(pool, repoinfos, nrepoinfos);
565   // FOR_REPOS(i, repo)
566   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
567   pool_addfileprovides(pool);
568   pool_createwhatprovides(pool);
569
570   queue_init(&job);
571   for (i = 2; i < argc; i++)
572     mkselect(pool, argv[i], 0, &job);
573   if (!job.count && mode == SOLVER_UPDATE)
574     updateall = 1;
575   else if (!job.count)
576     {
577       printf("nothing matched\n");
578       exit(1);
579     }
580
581   if (!mode)
582     {
583       /* show mode, no solver needed */
584       for (i = 0; i < job.count; i += 2)
585         {
586           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
587             {
588               Solvable *s = pool_id2solvable(pool, p);
589               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
590             }
591         }
592       exit(0);
593     }
594
595   // add mode
596   for (i = 0; i < job.count; i += 2)
597     job.elements[i] |= mode;
598
599   // multiversion test
600   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
601   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
602   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
603
604 rerunsolver:
605   for (;;)
606     {
607       Id problem, solution;
608       int pcnt, scnt;
609
610       solv = solver_create(pool);
611       solv->ignorealreadyrecommended = 1;
612       solv->updatesystem = updateall;
613       solver_solve(solv, &job);
614       if (!solv->problems.count)
615         break;
616       pcnt = solver_problem_count(solv);
617       printf("Found %d problems:\n", pcnt);
618       for (problem = 1; problem <= pcnt; problem++)
619         {
620           int take = 0;
621           printf("Problem %d:\n", problem);
622           solver_printprobleminfo(solv, problem);
623           printf("\n");
624           scnt = solver_solution_count(solv, problem);
625           for (solution = 1; solution <= pcnt; solution++)
626             {
627               printf("Solution %d:\n", solution);
628               solver_printsolution(solv, problem, solution);
629               printf("\n");
630             }
631           for (;;)
632             {
633               printf("Please choose a solution: ");
634               fflush(stdout);
635               *inbuf = 0;
636               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
637                 {
638                   printf("Abort.\n");
639                   exit(1);
640                 }
641               while (*ip == ' ' || *ip == '\t')
642                 ip++;
643               if (*ip >= '0' && *ip <= '9')
644                 {
645                   take = atoi(ip);
646                   if (take >= 1 && take <= solution)
647                     break;
648                 }
649               if (*ip == 's')
650                 {
651                   take = 0;
652                   break;
653                 }
654               if (*ip == 'q')
655                 {
656                   printf("Abort.\n");
657                   exit(1);
658                 }
659             }
660           if (!take)
661             continue;
662           solver_take_solution(solv, problem, take, &job);
663         }
664       solver_free(solv);
665       solv = 0;
666     }
667   if (!solv->trans.steps.count)
668     {
669       printf("Nothing to do.\n");
670       exit(1);
671     }
672   printf("\n");
673   printf("Transaction summary:\n\n");
674   solver_printtransaction(solv);
675   if (!yesno("OK to continue (y/n)? "))
676     {
677       printf("Abort.\n");
678       exit(1);
679     }
680
681   trans = &solv->trans;
682   queue_init(&checkq);
683   newpkgs = transaction_installedresult(trans, &checkq);
684   newpkgsfps = 0;
685
686   if (newpkgs)
687     {
688       printf("Downloading %d packages\n", newpkgs);
689       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
690       for (i = 0; i < newpkgs; i++)
691         {
692           unsigned int medianr;
693           char *loc;
694           Solvable *s;
695           struct repoinfo *cinfo;
696
697           p = checkq.elements[i];
698           s = pool_id2solvable(pool, p);
699           cinfo = s->repo->appdata;
700           if (!cinfo)
701             {
702               printf("%s: no repository information\n", s->repo->name);
703               exit(1);
704             }
705           loc = solvable_get_location(s, &medianr);
706           if (cinfo->type == TYPE_SUSETAGS)
707             {
708               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
709               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
710             }
711           if ((newpkgsfps[i] = curlfopen(cinfo->baseurl, loc, 0)) == 0)
712             {
713               printf("%s: %s not found\n", s->repo->name, loc);
714               exit(1);
715             }
716         }
717     }
718
719   if (newpkgs)
720     {
721       Queue conflicts;
722
723       printf("Searching for file conflicts\n");
724       queue_init(&conflicts);
725       fcstate.rpmdbstate = 0;
726       fcstate.newpkgscnt = newpkgs;
727       fcstate.checkq = &checkq;
728       fcstate.newpkgsfps = newpkgsfps;
729       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
730       if (conflicts.count)
731         {
732           printf("\n");
733           for (i = 0; i < conflicts.count; i += 5)
734             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]));
735           printf("\n");
736           if (yesno("Re-run solver (y/n/q)? "))
737             {
738               for (i = 0; i < newpkgs; i++)
739                 fclose(newpkgsfps[i]);
740               newpkgsfps = sat_free(newpkgsfps);
741               solver_free(solv);
742               pool_add_fileconflicts_deps(pool, &conflicts);
743               pool_createwhatprovides(pool);    /* Hmm... */
744               goto rerunsolver;
745             }
746         }
747       queue_free(&conflicts);
748     }
749
750   printf("Committing transaction:\n\n");
751   transaction_order(trans, 0);
752   for (i = 0; i < trans->steps.count; i++)
753     {
754       char rpmname[256];
755       const char *evr, *evrp;
756       Solvable *s;
757       int j;
758       FILE *fp;
759
760       p = trans->steps.elements[i];
761       s = pool_id2solvable(pool, p);
762       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
763       switch(type)
764         {
765         case SOLVER_TRANSACTION_ERASE:
766           printf("erase %s\n", solvid2str(pool, p));
767           if (strlen(solvid2str(pool, p)) > 255)
768             continue;
769           evr = evrp = id2str(pool, s->evr);
770           while (*evrp >= '0' && *evrp <= '9')
771             evrp++;
772           if (evrp > evr && evrp[0] == ':' && evrp[1])
773             evr = evrp + 1;
774           sprintf(rpmname, "%s-%s.%s", id2str(pool, s->name), evr, id2str(pool, s->arch));
775           runrpm("-e", rpmname, -1);
776           break;
777         case SOLVER_TRANSACTION_INSTALL:
778         case SOLVER_TRANSACTION_MULTIINSTALL:
779           printf("install %s\n", solvid2str(pool, p));
780           for (j = 0; j < newpkgs; j++)
781             if (checkq.elements[j] == p)
782               break;
783           fp = j < newpkgs ? newpkgsfps[j] : 0;
784           rewind(fp);
785           lseek(fileno(fp), 0, SEEK_SET);
786           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
787           fclose(fp);
788           newpkgsfps[j] = 0;
789           break;
790         default:
791           break;
792         }
793     }
794
795   for (i = 0; i < newpkgs; i++)
796     if (newpkgsfps[i])
797       fclose(newpkgsfps[i]);
798   sat_free(newpkgsfps);
799   queue_free(&checkq);
800   solver_free(solv);
801   queue_free(&job);
802   pool_free(pool);
803   exit(0);
804 }