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