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