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