- add new flags for the load functions:
[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  * - vendor policy loading
12  * - soft locks file handling
13  * - multi version handling
14  */
15
16 #define _GNU_SOURCE
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <dirent.h>
21 #include <unistd.h>
22 #include <zlib.h>
23 #include <fcntl.h>
24 #include <assert.h>
25 #include <sys/utsname.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28
29 #include "pool.h"
30 #include "poolarch.h"
31 #include "repo.h"
32 #include "evr.h"
33 #include "util.h"
34 #include "solver.h"
35 #include "solverdebug.h"
36 #include "chksum.h"
37 #include "repo_solv.h"
38
39 #include "repo_write.h"
40 #include "repo_rpmdb.h"
41 #include "repo_products.h"
42 #include "repo_rpmmd.h"
43 #include "repo_susetags.h"
44 #include "repo_repomdxml.h"
45 #include "repo_updateinfoxml.h"
46 #include "repo_deltainfoxml.h"
47 #include "repo_content.h"
48 #include "pool_fileconflicts.h"
49
50
51 #ifdef FEDORA
52 # define REPOINFO_PATH "/etc/yum.repos.d"
53 #else
54 # define REPOINFO_PATH "/etc/zypp/repos.d"
55 # define PRODUCTS_PATH "/etc/products.d"
56 #endif
57
58 #define SOLVCACHE_PATH "/var/cache/solv"
59
60
61 struct repoinfo {
62   Repo *repo;
63
64   char *alias;
65   char *name;
66   int enabled;
67   int autorefresh;
68   char *baseurl;
69   char *metalink;
70   char *path;
71   int type;
72   int gpgcheck;
73   int priority;
74   int keeppackages;
75
76   unsigned char extcookie[32];
77 };
78
79 #ifdef FEDORA
80 char *
81 yum_substitute(Pool *pool, char *line)
82 {
83   char *p, *p2;
84
85   p = line;
86   while ((p2 = strchr(p, '$')) != 0)
87     {
88       if (!strncmp(p2, "$releasever", 11))
89         {
90           static char *releaseevr;
91           if (!releaseevr)
92             {
93               FILE *fp;
94               char buf[1024], *bp;
95
96               fp = popen("rpm --nodigest --nosignature -q --qf '%{VERSION}\n' --whatprovides redhat-release", "r");
97               fread(buf, 1, sizeof(buf), fp);
98               fclose(fp);
99               bp = buf;
100               while (*bp != ' ' && *bp != '\t' && *bp != '\n')
101                 bp++;
102               *bp = 0;
103               releaseevr = strdup(buf);
104             }
105           *p2 = 0;
106           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
107           p2 = p + (p2 - line);
108           line = p;
109           p = p2 + strlen(releaseevr);
110           continue;
111         }
112       if (!strncmp(p2, "$basearch", 9))
113         {
114           static char *basearch;
115           if (!basearch)
116             {
117               struct utsname un;
118               if (uname(&un))
119                 {
120                   perror("uname");
121                   exit(1);
122                 }
123               basearch = strdup(un.machine);
124               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
125                 basearch[1] = '3';
126             }
127           *p2 = 0;
128           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
129           p2 = p + (p2 - line);
130           line = p;
131           p = p2 + strlen(basearch);
132           continue;
133         }
134       p = p2 + 1;
135     }
136   return line;
137 }
138 #endif
139
140 #define TYPE_UNKNOWN    0
141 #define TYPE_SUSETAGS   1
142 #define TYPE_RPMMD      2
143 #define TYPE_PLAINDIR   3
144
145 static int
146 read_repoinfos_sort(const void *ap, const void *bp)
147 {
148   const struct repoinfo *a = ap;
149   const struct repoinfo *b = bp;
150   return strcmp(a->alias, b->alias);
151 }
152
153 struct repoinfo *
154 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
155 {
156   char buf[4096];
157   char buf2[4096], *kp, *vp, *kpe;
158   DIR *dir;
159   FILE *fp;
160   struct dirent *ent;
161   int l, rdlen;
162   struct repoinfo *repoinfos = 0, *cinfo;
163   int nrepoinfos = 0;
164
165   rdlen = strlen(reposdir);
166   dir = opendir(reposdir);
167   if (!dir)
168     {
169       *nrepoinfosp = 0;
170       return 0;
171     }
172   while ((ent = readdir(dir)) != 0)
173     {
174       l = strlen(ent->d_name);
175       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
176         continue;
177       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
178       if ((fp = fopen(buf, "r")) == 0)
179         {
180           perror(buf);
181           continue;
182         }
183       cinfo = 0;
184       while(fgets(buf2, sizeof(buf2), fp))
185         {
186           l = strlen(buf2);
187           if (l == 0)
188             continue;
189           while (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t')
190             buf2[--l] = 0;
191           kp = buf2;
192           while (*kp == ' ' || *kp == '\t')
193             kp++;
194           if (!*kp || *kp == '#')
195             continue;
196 #ifdef FEDORA
197           if (strchr(kp, '$'))
198             kp = yum_substitute(pool, kp);
199 #endif
200           if (*kp == '[')
201             {
202               vp = strrchr(kp, ']');
203               if (!vp)
204                 continue;
205               *vp = 0;
206               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
207               cinfo = repoinfos + nrepoinfos++;
208               memset(cinfo, 0, sizeof(*cinfo));
209               cinfo->alias = strdup(kp + 1);
210               cinfo->type = TYPE_RPMMD;
211               cinfo->autorefresh = 1;
212               continue;
213             }
214           if (!cinfo)
215             continue;
216           vp = strchr(kp, '=');
217           if (!vp)
218             continue;
219           for (kpe = vp - 1; kpe >= kp; kpe--)
220             if (*kpe != ' ' && *kpe != '\t')
221               break;
222           if (kpe == kp)
223             continue;
224           vp++;
225           while (*vp == ' ' || *vp == '\t')
226             vp++;
227           kpe[1] = 0;
228           if (!strcmp(kp, "name"))
229             cinfo->name = strdup(vp);
230           else if (!strcmp(kp, "enabled"))
231             cinfo->enabled = *vp == '0' ? 0 : 1;
232           else if (!strcmp(kp, "autorefresh"))
233             cinfo->autorefresh = *vp == '0' ? 0 : 1;
234           else if (!strcmp(kp, "gpgcheck"))
235             cinfo->gpgcheck = *vp == '0' ? 0 : 1;
236           else if (!strcmp(kp, "baseurl"))
237             cinfo->baseurl = strdup(vp);
238           else if (!strcmp(kp, "mirrorlist"))
239             {
240               if (strstr(vp, "metalink"))
241                 cinfo->metalink = strdup(vp);
242             }
243           else if (!strcmp(kp, "path"))
244             {
245               if (vp && strcmp(vp, "/") != 0)
246                 cinfo->path = strdup(vp);
247             }
248           else if (!strcmp(kp, "type"))
249             {
250               if (!strcmp(vp, "yast2"))
251                 cinfo->type = TYPE_SUSETAGS;
252               else if (!strcmp(vp, "rpm-md"))
253                 cinfo->type = TYPE_RPMMD;
254               else if (!strcmp(vp, "plaindir"))
255                 cinfo->type = TYPE_PLAINDIR;
256               else
257                 cinfo->type = TYPE_UNKNOWN;
258             }
259           else if (!strcmp(kp, "priority"))
260             cinfo->priority = atoi(vp);
261           else if (!strcmp(kp, "keeppackages"))
262             cinfo->keeppackages = *vp == '0' ? 0 : 1;
263         }
264       fclose(fp);
265       cinfo = 0;
266     }
267   closedir(dir);
268   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
269   *nrepoinfosp = nrepoinfos;
270   return repoinfos;
271 }
272
273 static ssize_t
274 cookie_gzread(void *cookie, char *buf, size_t nbytes)
275 {
276   return gzread((gzFile *)cookie, buf, nbytes);
277 }
278
279 static int
280 cookie_gzclose(void *cookie)
281 {
282   return gzclose((gzFile *)cookie);
283 }
284
285 static inline int
286 opentmpfile()
287 {
288   char tmpl[100];
289   int fd;
290
291   strcpy(tmpl, "/var/tmp/solvXXXXXX");
292   fd = mkstemp(tmpl);
293   if (fd < 0)
294     {
295       perror("mkstemp");
296       exit(1);
297     }
298   unlink(tmpl);
299   return fd;
300 }
301
302 static int
303 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
304 {
305   char buf[1024];
306   unsigned char *sum;
307   void *h;
308   int l;
309
310   h = sat_chksum_create(chksumtype);
311   if (!h)
312     {
313       printf("%s: unknown checksum type\n", file);
314       return 0;
315     }
316   while ((l = read(fd, buf, sizeof(buf))) > 0)
317     sat_chksum_add(h, buf, l);
318   lseek(fd, 0, SEEK_SET);
319   l = 0;
320   sum = sat_chksum_get(h, &l);
321   if (memcmp(sum, chksum, l))
322     {
323       printf("%s: checksum mismatch\n", file);
324       return 0;
325     }
326   return 1;
327 }
328
329 char *
330 findmetalinkurl(FILE *fp)
331 {
332   char buf[4096], *bp, *ep;
333   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
334     {
335       while (*bp == ' ' || *bp == '\t')
336         bp++;
337       if (strncmp(bp, "<url", 4))
338         continue;
339       bp = strchr(bp, '>');
340       if (!bp)
341         continue;
342       bp++;
343       ep = strstr(bp, "repodata/repomd.xml</url>");
344       if (!ep)
345         continue;
346       *ep = 0;
347       if (strncmp(bp, "http", 4))
348         continue;
349       return strdup(bp);
350     }
351   return 0;
352 }
353
354 FILE *
355 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype)
356 {
357   pid_t pid;
358   int fd, l;
359   int status;
360   char url[4096];
361   const char *baseurl = cinfo->baseurl;
362
363   if (!baseurl)
364     {
365       if (!cinfo->metalink)
366         return 0;
367       if (file != cinfo->metalink)
368         {
369           FILE *fp = curlfopen(cinfo, cinfo->metalink, 0, 0, 0);
370           if (!fp)
371             return 0;
372           cinfo->baseurl = findmetalinkurl(fp);
373           fclose(fp);
374           if (!cinfo->baseurl)
375             return 0;
376           return curlfopen(cinfo, file, uncompress, chksum, chksumtype);
377         }
378       snprintf(url, sizeof(url), "%s", file);
379     }
380   else
381     {
382       l = strlen(baseurl);
383       if (l && baseurl[l - 1] == '/')
384         snprintf(url, sizeof(url), "%s%s", baseurl, file);
385       else
386         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
387     }
388   fd = opentmpfile();
389   if ((pid = fork()) == (pid_t)-1)
390     {
391       perror("fork");
392       exit(1);
393     }
394   if (pid == 0)
395     {
396       if (fd != 1)
397         {
398           dup2(fd, 1);
399           close(fd);
400         }
401       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
402       perror("curl");
403       _exit(0);
404     }
405   while (waitpid(pid, &status, 0) != pid)
406     ;
407   if (lseek(fd, 0, SEEK_END) == 0)
408     {
409       /* empty file */
410       close(fd);
411       return 0;
412     }
413   lseek(fd, 0, SEEK_SET);
414   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
415     {
416       close(fd);
417       return 0;
418     }
419   if (uncompress)
420     {
421       char tmpl[100];
422       cookie_io_functions_t cio;
423       gzFile *gzf;
424
425       sprintf(tmpl, "/dev/fd/%d", fd);
426       gzf = gzopen(tmpl, "r");
427       close(fd);
428       if (!gzf)
429         return 0;
430       memset(&cio, 0, sizeof(cio));
431       cio.read = cookie_gzread;
432       cio.close = cookie_gzclose;
433       return fopencookie(gzf, "r", cio);
434     }
435   fcntl(fd, F_SETFD, FD_CLOEXEC);
436   return fdopen(fd, "r");
437 }
438
439 void
440 cleanupgpg(char *gpgdir)
441 {
442   char cmd[256];
443   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
444   unlink(cmd);
445   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
446   unlink(cmd);
447   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
448   unlink(cmd);
449   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
450   unlink(cmd);
451   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
452   unlink(cmd);
453   rmdir(gpgdir);
454 }
455
456 int
457 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
458 {
459   char *gpgdir;
460   char *keysfile;
461   const char *pubkey;
462   char cmd[256];
463   FILE *kfp;
464   Solvable *s;
465   Id p;
466   off_t posfp, possigfp;
467   int r, nkeys;
468
469   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
470   if (!gpgdir)
471     return 0;
472   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
473   if (!(kfp = fopen(keysfile, "w")) )
474     {
475       cleanupgpg(gpgdir);
476       return 0;
477     }
478   nkeys = 0;
479   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
480     {
481       if (!s->repo)
482         continue;
483       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
484       if (!pubkey || !*pubkey)
485         continue;
486       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
487         break;
488       if (fputc('\n', kfp) == EOF)      /* Just in case... */
489         break;
490       nkeys++;
491     }
492   if (fclose(kfp) || !nkeys)
493     {
494       cleanupgpg(gpgdir);
495       return 0;
496     }
497   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
498   if (system(cmd))
499     {
500       fprintf(stderr, "key import error\n");
501       cleanupgpg(gpgdir);
502       return 0;
503     }
504   unlink(keysfile);
505   posfp = lseek(fileno(fp), 0, SEEK_CUR);
506   lseek(fileno(fp), 0, SEEK_SET);
507   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
508   lseek(fileno(sigfp), 0, SEEK_SET);
509   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
510   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
511   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
512   r = system(cmd);
513   lseek(fileno(sigfp), possigfp, SEEK_SET);
514   lseek(fileno(fp), posfp, SEEK_SET);
515   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
516   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
517   cleanupgpg(gpgdir);
518   return r == 0 ? 1 : 0;
519 }
520
521 #define CHKSUM_IDENT "1.1"
522
523 void
524 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
525 {
526   char buf[4096];
527   void *h = sat_chksum_create(chktype);
528   int l;
529
530   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
531   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
532     sat_chksum_add(h, buf, l);
533   rewind(fp);
534   sat_chksum_free(h, out);
535 }
536
537 void
538 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
539 {
540   void *h = sat_chksum_create(chktype);
541   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
542   sat_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
543   sat_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
544   sat_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
545   sat_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
546   sat_chksum_free(h, out);
547 }
548
549 void
550 setarch(Pool *pool)
551 {
552   struct utsname un;
553   if (uname(&un))
554     {
555       perror("uname");
556       exit(1);
557     }
558   pool_setarch(pool, un.machine);
559 }
560
561 char *calccachepath(Repo *repo, const char *repoext)
562 {
563   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
564   if (repoext)
565     {
566       p = pool_tmpjoin(repo->pool, p, "_", repoext);
567       p = pool_tmpjoin(repo->pool, p, ".solvx", 0);
568     }
569   else
570     p = pool_tmpjoin(repo->pool, p, ".solv", 0);
571   q = p + strlen(SOLVCACHE_PATH) + 1;
572   if (*q == '.')
573     *q = '_';
574   for (; *q; q++)
575     if (*q == '/')
576       *q = '_';
577   return p;
578 }
579
580 int
581 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie)
582 {
583   FILE *fp;
584   unsigned char mycookie[32];
585   struct repoinfo *cinfo;
586   int flags = 0;
587
588   cinfo = repo->appdata;
589   if (!(fp = fopen(calccachepath(repo, repoext), "r")))
590     return 0;
591   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
592     {
593       fclose(fp);
594       return 0;
595     }
596   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
597     {
598       fclose(fp);
599       return 0;
600     }
601   rewind(fp);
602   if (repoext && !strcmp(repoext, "DL"))
603     flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
604   else if (repoext)
605     flags = REPO_USE_LOADING|REPO_LOCALPOOL|REPO_EXTEND_SOLVABLES;
606
607   if (repo_add_solv_flags(repo, fp, flags))
608     {
609       fclose(fp);
610       return 0;
611     }
612   if (!repoext && cinfo)
613     {
614       struct stat stb;
615       if (!fstat(fileno(fp), &stb))
616         calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
617     }
618   fclose(fp);
619   return 1;
620 }
621
622 int
623 myrepodatafilter(Repo *repo, Repokey *key, void *kfdata)
624 {
625   Repodata *data = kfdata;
626
627   if (key->name == 1 && key->size != data - repo->repodata)
628     return -1;
629   if (key->storage == KEY_STORAGE_SOLVABLE)
630     return KEY_STORAGE_DROPPED;
631   return repo_write_stdkeyfilter(repo, key, kfdata);
632 }
633
634 static void
635 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
636 {
637   Id *addedfileprovides = 0;
638   FILE *fp;
639   int i, fd;
640   char *tmpl;
641   int myinfo = 0;
642   struct repoinfo *cinfo;
643
644   cinfo = repo->appdata;
645   mkdir(SOLVCACHE_PATH, 0755);
646   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
647   fd = mkstemp(tmpl);
648   if (fd < 0)
649     {
650       free(tmpl);
651       return;
652     }
653   fchmod(fd, 0444);
654   if (!(fp = fdopen(fd, "w")))
655     {
656       close(fd);
657       unlink(tmpl);
658       free(tmpl);
659       return;
660     }
661   if (!repoext)
662     {
663       if (!info)
664         {
665           info = repo_add_repodata(repo, 0);
666           myinfo = 1;
667         }
668       pool_addfileprovides_ids(repo->pool, 0, &addedfileprovides);
669       if (addedfileprovides && *addedfileprovides)
670         {
671           for (i = 0; addedfileprovides[i]; i++)
672             repodata_add_idarray(info, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[i]);
673         }
674       sat_free(addedfileprovides);
675       repodata_internalize(info);
676       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
677     }
678   else
679     repo_write(repo, fp, myrepodatafilter, info, 0);
680   if (myinfo)
681     repodata_free(info);
682   if (fwrite(cookie, 32, 1, fp) != 1)
683     {
684       fclose(fp);
685       unlink(tmpl);
686       free(tmpl);
687       return;
688     }
689   if (fclose(fp))
690     {
691       unlink(tmpl);
692       free(tmpl);
693       return;
694     }
695   if (!repoext && cinfo)
696     {
697       struct stat stb;
698       if (!stat(tmpl, &stb))
699         calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
700     }
701   if (!rename(tmpl, calccachepath(repo, repoext)))
702     unlink(tmpl);
703   free(tmpl);
704 }
705
706 static Pool *
707 read_sigs()
708 {
709   Pool *sigpool = pool_create();
710   Repo *repo = repo_create(sigpool, "rpmdbkeys");
711   repo_add_rpmdb_pubkeys(repo, 0, 0);
712   return sigpool;
713 }
714
715 static inline int
716 iscompressed(const char *name)
717 {
718   int l = strlen(name);
719   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
720 }
721
722 static inline const char *
723 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
724 {
725   Pool *pool = repo->pool;
726   Dataiterator di;
727   const char *filename;
728
729   filename = 0;
730   *chksump = 0;
731   *chksumtypep = 0;
732   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
733   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
734   if (dataiterator_step(&di))
735     {
736       dataiterator_setpos_parent(&di);
737       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
738       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
739     }
740   dataiterator_free(&di);
741   if (filename && !*chksumtypep)
742     {
743       printf("no %s file checksum!\n", what);
744       filename = 0;
745     }
746   return filename;
747 }
748
749 int
750 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
751 {
752   Pool *pool = repo->pool;
753   Dataiterator di;
754   Id chksumtype, handle;
755   const unsigned char *chksum;
756   const char *filename;
757
758   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
759   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
760   if (!dataiterator_step(&di))
761     {
762       dataiterator_free(&di);
763       return 0;
764     }
765   if (!strcmp(what, "prestodelta"))
766     what = "deltainfo";
767   dataiterator_setpos_parent(&di);
768   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
769   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
770   if (!filename || !chksum)
771     {
772       dataiterator_free(&di);
773       return 0;
774     }
775   handle = repodata_new_handle(data);
776   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
777   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
778   if (chksumtype)
779     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
780   if (!strcmp(what, "deltainfo"))
781     {
782       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
783       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
784       repodata_add_idarray(data, handle, REPOSITORY_KEYS, DELTA_PACKAGE_NAME);
785       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_ID);
786       repodata_add_idarray(data, handle, REPOSITORY_KEYS, DELTA_PACKAGE_EVR);
787       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_ID);
788       repodata_add_idarray(data, handle, REPOSITORY_KEYS, DELTA_PACKAGE_ARCH);
789       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_ID);
790       repodata_add_idarray(data, handle, REPOSITORY_KEYS, DELTA_BASE_EVR);
791       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_ID);
792     }
793   if (!strcmp(what, "filelists"))
794     {
795       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
796       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
797     }
798   dataiterator_free(&di);
799   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
800   return 1;
801 }
802
803 static inline const char *
804 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
805 {
806   Pool *pool = repo->pool;
807   Dataiterator di;
808   const char *filename;
809
810   filename = 0;
811   *chksump = 0;
812   *chksumtypep = 0;
813   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
814   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
815   if (dataiterator_step(&di))
816     {
817       dataiterator_setpos_parent(&di);
818       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
819       filename = what;
820     }
821   dataiterator_free(&di);
822   if (filename && !*chksumtypep)
823     {
824       printf("no %s file checksum!\n", what);
825       filename = 0;
826     }
827   return filename;
828 }
829
830
831 int
832 load_stub(Pool *pool, Repodata *data, void *dp)
833 {
834   const char *filename, *descrdir, *repomdtype;
835   const unsigned char *filechksum;
836   Id filechksumtype;
837   struct repoinfo *cinfo;
838   FILE *fp;
839   Id defvendor;
840   char ext[3];
841
842   cinfo = data->repo->appdata;
843
844   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
845   if (filename)
846     {
847       /* susetags load */
848       ext[0] = filename[9];
849       ext[1] = filename[10];
850       ext[2] = 0;
851 #if 1
852       printf("[%s:%s", data->repo->name, ext);
853 #endif
854       if (usecachedrepo(data->repo, ext, cinfo->extcookie))
855         {
856           printf(" cached]"); fflush(stdout);
857           return 1;
858         }
859 #if 1
860       printf(" loading]"); fflush(stdout);
861 #endif
862       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
863       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
864       if (!descrdir)
865         descrdir = "suse/setup/descr";
866       filechksumtype = 0;
867       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
868       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
869         return 0;
870       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
871       fclose(fp);
872       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
873       return 1;
874     }
875
876   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
877   if (repomdtype)
878     {
879       if (!strcmp(repomdtype, "filelists"))
880         strcpy(ext, "FL");
881       else if (!strcmp(repomdtype, "deltainfo"))
882         strcpy(ext, "DL");
883       else
884         return 0;
885 #if 1
886       printf("[%s:%s", data->repo->name, ext);
887 #endif
888       if (usecachedrepo(data->repo, ext, cinfo->extcookie))
889         {
890           printf(" cached]");fflush(stdout);
891           return 1;
892         }
893       printf(" loading]"); fflush(stdout);
894       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
895       filechksumtype = 0;
896       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
897       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) == 0)
898         return 0;
899       if (!strcmp(ext, "FL"))
900         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
901       else if (!strcmp(ext, "DL"))
902         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
903       fclose(fp);
904       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
905       return 1;
906     }
907
908   return 0;
909 }
910
911 Id susetags_langtags[] = {
912   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
913   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
914   SOLVABLE_EULA, REPOKEY_TYPE_STR,
915   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
916   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
917   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
918   0, 0
919 };
920
921 void
922 susetags_add_ext(Repo *repo, Repodata *data)
923 {
924   Pool *pool = repo->pool;
925   Dataiterator di;
926   char ext[3];
927   Id handle, filechksumtype;
928   const unsigned char *filechksum;
929   int i;
930
931   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
932   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
933   while (dataiterator_step(&di))
934     {
935       if (strncmp(di.kv.str, "packages.", 9) != 0)
936         continue;
937       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
938         continue;
939       ext[0] = di.kv.str[9];
940       ext[1] = di.kv.str[10];
941       ext[2] = 0;
942       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
943         continue;
944       handle = repodata_new_handle(data);
945       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
946       if (filechksumtype)
947         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
948       if (!strcmp(ext, "DU"))
949         {
950           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
951           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
952         }
953       if (!strcmp(ext, "FL"))
954         {
955           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
956           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
957         }
958       else
959         {
960           for (i = 0; susetags_langtags[i]; i += 2)
961             {
962               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
963               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
964             }
965         }
966       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
967     }
968   dataiterator_free(&di);
969 }
970
971 void
972 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
973 {
974   Repo *repo;
975   struct repoinfo *cinfo;
976   int i;
977   FILE *fp;
978   FILE *sigfp;
979   const char *filename;
980   const unsigned char *filechksum;
981   Id filechksumtype;
982   const char *descrdir;
983   int defvendor;
984   struct stat stb;
985   unsigned char cookie[32];
986   Pool *sigpool = 0;
987   Repodata *data;
988
989   repo = repo_create(pool, "@System");
990   printf("rpm database:");
991   if (stat("/var/lib/rpm/Packages", &stb))
992     memset(&stb, 0, sizeof(&stb));
993   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie);
994   if (usecachedrepo(repo, 0, cookie))
995     printf(" cached\n");
996   else
997     {
998       FILE *ofp;
999       printf(" reading\n");
1000       int done = 0;
1001
1002 #ifdef PRODUCTS_PATH
1003       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1004 #endif
1005       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1006         {
1007           Repo *ref = repo_create(pool, "@System.old");
1008           if (!repo_add_solv(ref, ofp))
1009             {
1010               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1011               done = 1;
1012             }
1013           fclose(ofp);
1014           repo_free(ref, 1);
1015         }
1016       if (!done)
1017         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1018       writecachedrepo(repo, 0, 0, cookie);
1019     }
1020   pool_set_installed(pool, repo);
1021
1022   for (i = 0; i < nrepoinfos; i++)
1023     {
1024       cinfo = repoinfos + i;
1025       if (!cinfo->enabled)
1026         continue;
1027
1028       repo = repo_create(pool, cinfo->alias);
1029       cinfo->repo = repo;
1030       repo->appdata = cinfo;
1031       repo->priority = 99 - cinfo->priority;
1032
1033       if (!cinfo->autorefresh && usecachedrepo(repo, 0, 0))
1034         {
1035           printf("repo '%s':", cinfo->alias);
1036           printf(" cached\n");
1037           continue;
1038         }
1039       switch (cinfo->type)
1040         {
1041         case TYPE_RPMMD:
1042           printf("rpmmd repo '%s':", cinfo->alias);
1043           fflush(stdout);
1044           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0)) == 0)
1045             {
1046               printf(" no repomd.xml file, skipped\n");
1047               repo_free(repo, 1);
1048               cinfo->repo = 0;
1049               break;
1050             }
1051           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
1052           if (usecachedrepo(repo, 0, cookie))
1053             {
1054               printf(" cached\n");
1055               fclose(fp);
1056               break;
1057             }
1058           sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0);
1059 #ifndef FEDORA
1060           if (!sigfp)
1061             {
1062               printf(" unsigned, skipped\n");
1063               fclose(fp);
1064               break;
1065             }
1066 #endif
1067           if (sigfp)
1068             {
1069               if (!sigpool)
1070                 sigpool = read_sigs();
1071               if (!checksig(sigpool, fp, sigfp))
1072                 {
1073                   printf(" checksig failed, skipped\n");
1074                   fclose(sigfp);
1075                   fclose(fp);
1076                   break;
1077                 }
1078               fclose(sigfp);
1079             }
1080           repo_add_repomdxml(repo, fp, 0);
1081           fclose(fp);
1082           printf(" reading\n");
1083           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1084           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
1085             {
1086               repo_add_rpmmd(repo, fp, 0, 0);
1087               fclose(fp);
1088             }
1089
1090           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1091           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype)) != 0)
1092             {
1093               repo_add_updateinfoxml(repo, fp, 0);
1094               fclose(fp);
1095             }
1096
1097           data = repo_last_repodata(repo);
1098           if (!repomd_add_ext(repo, data, "deltainfo"))
1099             repomd_add_ext(repo, data, "prestodelta");
1100           repomd_add_ext(repo, data, "filelists");
1101           repodata_internalize(data);
1102           writecachedrepo(repo, data, 0, cookie);
1103           repodata_create_stubs(data);
1104           break;
1105
1106         case TYPE_SUSETAGS:
1107           printf("susetags repo '%s':", cinfo->alias);
1108           fflush(stdout);
1109           descrdir = 0;
1110           defvendor = 0;
1111           if ((fp = curlfopen(cinfo, "content", 0, 0, 0)) == 0)
1112             {
1113               printf(" no content file, skipped\n");
1114               repo_free(repo, 1);
1115               cinfo->repo = 0;
1116               break;
1117             }
1118           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cookie);
1119           if (usecachedrepo(repo, 0, cookie))
1120             {
1121               printf(" cached\n");
1122               fclose(fp);
1123               break;
1124             }
1125           sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0);
1126           if (!sigfp)
1127             {
1128               printf(" unsigned, skipped\n");
1129               fclose(fp);
1130               break;
1131             }
1132           if (sigfp)
1133             {
1134               if (!sigpool)
1135                 sigpool = read_sigs();
1136               if (!checksig(sigpool, fp, sigfp))
1137                 {
1138                   printf(" checksig failed, skipped\n");
1139                   fclose(sigfp);
1140                   fclose(fp);
1141                   break;
1142                 }
1143               fclose(sigfp);
1144             }
1145           repo_add_content(repo, fp, 0);
1146           fclose(fp);
1147           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1148           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1149           if (!descrdir)
1150             descrdir = "suse/setup/descr";
1151           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1152           if (!filename)
1153             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1154           if (!filename)
1155             {
1156               printf(" no packages file entry, skipped\n");
1157               break;
1158             }
1159           printf(" reading\n");
1160           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype)) == 0)
1161             break;
1162           repo_add_susetags(repo, fp, defvendor, 0, 0);
1163           fclose(fp);
1164           data = repo_last_repodata(repo);
1165           susetags_add_ext(repo, data);
1166           repodata_internalize(data);
1167           writecachedrepo(repo, data, 0, cookie);
1168           repodata_create_stubs(data);
1169           break;
1170         default:
1171           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1172           repo_free(repo, 1);
1173           cinfo->repo = 0;
1174           break;
1175         }
1176     }
1177   if (sigpool)
1178     pool_free(sigpool);
1179 }
1180
1181 void
1182 mkselect(Pool *pool, const char *arg, int flags, Queue *out)
1183 {
1184   Id id, p, pp;
1185   Id type = 0;
1186   const char *r, *r2;
1187
1188   id = str2id(pool, arg, 0);
1189   if (id)
1190     {
1191       FOR_PROVIDES(p, pp, id)
1192         {
1193           Solvable *s = pool_id2solvable(pool, p);
1194           if (s->name == id)
1195             {
1196               type = SOLVER_SOLVABLE_NAME;
1197               break;
1198             }
1199           type = SOLVER_SOLVABLE_PROVIDES;
1200         }
1201     }
1202   if (!type)
1203     {
1204       /* did not find a solvable, see if it's a relation */
1205       if ((r = strpbrk(arg, "<=>")) != 0)
1206         {
1207           Id rid, rname, revr;
1208           int rflags = 0;
1209           for (r2 = r; r2 > arg && (r2[-1] == ' ' || r2[-1] == '\t'); )
1210             r2--;
1211           rname = r2 > arg ? strn2id(pool, arg, r2 - arg, 1) : 0;
1212           for (; *r; r++)
1213             {
1214               if (*r == '<')
1215                 rflags |= REL_LT;
1216               else if (*r == '=')
1217                 rflags |= REL_EQ;
1218               else if (*r == '>')
1219                 rflags |= REL_GT;
1220               else
1221                 break;
1222             }
1223           while (*r == ' ' || *r == '\t')
1224             r++;
1225           revr = *r ? str2id(pool, r, 1) : 0;
1226           rid = rname && revr ? rel2id(pool, rname, revr, rflags, 1) : 0;
1227           if (rid)
1228             {
1229               FOR_PROVIDES(p, pp, rid)
1230                 {
1231                   Solvable *s = pool_id2solvable(pool, p);
1232                   if (pool_match_nevr(pool, s, rid))
1233                     {
1234                       type = SOLVER_SOLVABLE_NAME;
1235                       break;
1236                     }
1237                   type = SOLVER_SOLVABLE_PROVIDES;
1238                 }
1239             }
1240           if (type)
1241             id = rid;
1242         }
1243     }
1244   if (type)
1245     {
1246       queue_push(out, type);
1247       queue_push(out, id);
1248     }
1249 }
1250
1251 int
1252 yesno(const char *str)
1253 {
1254   char inbuf[128], *ip;
1255
1256   for (;;)
1257     {
1258       printf("%s", str);
1259       fflush(stdout);
1260       *inbuf = 0;
1261       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1262         {
1263           printf("Abort.\n");
1264           exit(1);
1265         }
1266       while (*ip == ' ' || *ip == '\t')
1267         ip++;
1268       if (*ip == 'q')
1269         {
1270           printf("Abort.\n");
1271           exit(1);
1272         }
1273       if (*ip == 'y' || *ip == 'n')
1274         return *ip == 'y' ? 1 : 0;
1275     }
1276 }
1277
1278 struct fcstate {
1279   FILE **newpkgsfps;
1280   Queue *checkq;
1281   int newpkgscnt;
1282   void *rpmdbstate;
1283 };
1284
1285 static void *
1286 fc_cb(Pool *pool, Id p, void *cbdata)
1287 {
1288   struct fcstate *fcstate = cbdata;
1289   Solvable *s;
1290   Id rpmdbid;
1291   int i;
1292   FILE *fp;
1293
1294   if (!p)
1295     {
1296       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
1297       return 0;
1298     }
1299   s = pool_id2solvable(pool, p);
1300   if (pool->installed && s->repo == pool->installed)
1301     {
1302       if (!s->repo->rpmdbid)
1303         return 0;
1304       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
1305       if (!rpmdbid)
1306         return 0;
1307        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
1308     }
1309   for (i = 0; i < fcstate->newpkgscnt; i++)
1310     if (fcstate->checkq->elements[i] == p)
1311       break;
1312   if (i == fcstate->newpkgscnt)
1313     return 0;
1314   fp = fcstate->newpkgsfps[i];
1315   if (!fp)
1316     return 0;
1317   rewind(fp);
1318   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
1319 }
1320
1321 void
1322 runrpm(const char *arg, const char *name, int dupfd3)
1323 {
1324   pid_t pid;
1325   int status;
1326
1327   if ((pid = fork()) == (pid_t)-1)
1328     {
1329       perror("fork");
1330       exit(1);
1331     }
1332   if (pid == 0)
1333     {
1334       if (dupfd3 != -1 && dupfd3 != 3)
1335         {
1336           dup2(dupfd3, 3);
1337           close(dupfd3);
1338         }
1339       if (dupfd3 != -1)
1340         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
1341       if (strcmp(arg, "-e") == 0)
1342         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1343       else
1344         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
1345       perror("rpm");
1346       _exit(0);
1347     }
1348   while (waitpid(pid, &status, 0) != pid)
1349     ;
1350   if (status)
1351     {
1352       printf("rpm failed\n");
1353       exit(1);
1354     }
1355 }
1356
1357 static Id
1358 nscallback(Pool *pool, void *data, Id name, Id evr)
1359 {
1360   if (name == NAMESPACE_PRODUCTBUDDY)
1361     {    
1362       /* SUSE specific hack: each product has an associated rpm */
1363       Solvable *s = pool->solvables + evr; 
1364       Id p, pp, cap; 
1365       
1366       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
1367       if (!cap)
1368         return 0;
1369       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
1370       if (!cap)
1371         return 0;
1372       FOR_PROVIDES(p, pp, cap) 
1373         {
1374           Solvable *ps = pool->solvables + p; 
1375           if (ps->repo == s->repo && ps->arch == s->arch)
1376             break;
1377         }
1378       return p;
1379     }
1380   return 0;
1381 }
1382
1383
1384 int
1385 main(int argc, char **argv)
1386 {
1387   Pool *pool;
1388   Id p, pp;
1389   struct repoinfo *repoinfos;
1390   int nrepoinfos = 0;
1391   int i, mode, newpkgs;
1392   Queue job, checkq;
1393   Solver *solv = 0;
1394   Transaction *trans;
1395   char inbuf[128], *ip;
1396   int updateall = 0;
1397   int distupgrade = 0;
1398   int patchmode = 0;
1399   FILE **newpkgsfps;
1400   struct fcstate fcstate;
1401
1402   argc--;
1403   argv++;
1404   if (!argv[0])
1405     {
1406       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1407       exit(1);
1408     }
1409   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
1410     mode = SOLVER_INSTALL;
1411   else if (!strcmp(argv[0], "patch"))
1412     {
1413       mode = SOLVER_UPDATE;
1414       patchmode = 1;
1415     }
1416   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
1417     mode = SOLVER_ERASE;
1418   else if (!strcmp(argv[0], "show"))
1419     mode = 0;
1420   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
1421     mode = SOLVER_UPDATE;
1422   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
1423     {
1424       mode = SOLVER_UPDATE;
1425       distupgrade = 1;
1426     }
1427   else
1428     {
1429       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
1430       exit(1);
1431     }
1432
1433   pool = pool_create();
1434   pool_setloadcallback(pool, load_stub, 0);
1435   pool->nscallback = nscallback;
1436   // pool_setdebuglevel(pool, 2);
1437   setarch(pool);
1438   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
1439   read_repos(pool, repoinfos, nrepoinfos);
1440   // FOR_REPOS(i, repo)
1441   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
1442   pool_addfileprovides(pool);
1443   pool_createwhatprovides(pool);
1444
1445   queue_init(&job);
1446   for (i = 1; i < argc; i++)
1447     mkselect(pool, argv[i], 0, &job);
1448   if (!job.count && mode == SOLVER_UPDATE)
1449     updateall = 1;
1450   else if (!job.count)
1451     {
1452       printf("no package matched\n");
1453       exit(1);
1454     }
1455
1456   if (!mode)
1457     {
1458       /* show mode, no solver needed */
1459       for (i = 0; i < job.count; i += 2)
1460         {
1461           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
1462             {
1463               Solvable *s = pool_id2solvable(pool, p);
1464               const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de");
1465               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
1466               if (sum)
1467                 printf("    %s\n", sum);
1468             }
1469         }
1470       exit(0);
1471     }
1472
1473   if (updateall && patchmode)
1474     {
1475       int pruneyou = 0;
1476       Map installedmap;
1477       Solvable *s;
1478
1479       map_init(&installedmap, pool->nsolvables);
1480       if (pool->installed)
1481         FOR_REPO_SOLVABLES(pool->installed, p, s)
1482           MAPSET(&installedmap, p);
1483
1484       /* install all patches */
1485       updateall = 0;
1486       mode = SOLVER_INSTALL;
1487       for (p = 1; p < pool->nsolvables; p++)
1488         {
1489           const char *type;
1490           int r;
1491           Id p2;
1492
1493           s = pool->solvables + p;
1494           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
1495             continue;
1496           FOR_PROVIDES(p2, pp, s->name)
1497             {
1498               Solvable *s2 = pool->solvables + p2;
1499               if (s2->name != s->name)
1500                 continue;
1501               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
1502               if (r < 0 || (r == 0 && p > p2))
1503                 break;
1504             }
1505           if (p2)
1506             continue;
1507           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
1508           if (type && !strcmp(type, "optional"))
1509             continue;
1510           r = solvable_trivial_installable_map(s, &installedmap, 0);
1511           if (r == -1)
1512             continue;
1513           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
1514             {
1515               if (!pruneyou++)
1516                 queue_empty(&job);
1517             }
1518           else if (pruneyou)
1519             continue;
1520           queue_push2(&job, SOLVER_SOLVABLE, p);
1521         }
1522       map_free(&installedmap);
1523     }
1524
1525   // add mode
1526   for (i = 0; i < job.count; i += 2)
1527     job.elements[i] |= mode;
1528
1529   // multiversion test
1530   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
1531   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
1532   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
1533
1534 rerunsolver:
1535   for (;;)
1536     {
1537       Id problem, solution;
1538       int pcnt, scnt;
1539
1540       solv = solver_create(pool);
1541       solv->ignorealreadyrecommended = 1;
1542       solv->updatesystem = updateall;
1543       solv->dosplitprovides = updateall;
1544       if (updateall && distupgrade)
1545         {
1546           solv->distupgrade = 1;
1547           solv->allowdowngrade = 1;
1548           solv->allowarchchange = 1;
1549           solv->allowvendorchange = 1;
1550         }
1551       // queue_push2(&job, SOLVER_DISTUPGRADE, 3);
1552       solver_solve(solv, &job);
1553       if (!solv->problems.count)
1554         break;
1555       pcnt = solver_problem_count(solv);
1556       printf("Found %d problems:\n", pcnt);
1557       for (problem = 1; problem <= pcnt; problem++)
1558         {
1559           int take = 0;
1560           printf("Problem %d:\n", problem);
1561           solver_printprobleminfo(solv, problem);
1562           printf("\n");
1563           scnt = solver_solution_count(solv, problem);
1564           for (solution = 1; solution <= scnt; solution++)
1565             {
1566               printf("Solution %d:\n", solution);
1567               solver_printsolution(solv, problem, solution);
1568               printf("\n");
1569             }
1570           for (;;)
1571             {
1572               printf("Please choose a solution: ");
1573               fflush(stdout);
1574               *inbuf = 0;
1575               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
1576                 {
1577                   printf("Abort.\n");
1578                   exit(1);
1579                 }
1580               while (*ip == ' ' || *ip == '\t')
1581                 ip++;
1582               if (*ip >= '0' && *ip <= '9')
1583                 {
1584                   take = atoi(ip);
1585                   if (take >= 1 && take <= scnt)
1586                     break;
1587                 }
1588               if (*ip == 's')
1589                 {
1590                   take = 0;
1591                   break;
1592                 }
1593               if (*ip == 'q')
1594                 {
1595                   printf("Abort.\n");
1596                   exit(1);
1597                 }
1598             }
1599           if (!take)
1600             continue;
1601           solver_take_solution(solv, problem, take, &job);
1602         }
1603       solver_free(solv);
1604       solv = 0;
1605     }
1606
1607   trans = &solv->trans;
1608   if (!trans->steps.count)
1609     {
1610       printf("Nothing to do.\n");
1611       exit(1);
1612     }
1613   printf("\n");
1614   printf("Transaction summary:\n\n");
1615   solver_printtransaction(solv);
1616
1617 #if 1
1618   if (1)
1619     {
1620       DUChanges duc[4];
1621       int i;
1622
1623       duc[0].path = "/";
1624       duc[1].path = "/usr/share/man";
1625       duc[2].path = "/sbin";
1626       duc[3].path = "/etc";
1627       transaction_calc_duchanges(trans, duc, 4);
1628       for (i = 0; i < 4; i++)
1629         printf("duchanges %s: %d K  %d\n", duc[i].path, duc[i].kbytes, duc[i].files);
1630       printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
1631     }
1632 #endif
1633
1634   if (!yesno("OK to continue (y/n)? "))
1635     {
1636       printf("Abort.\n");
1637       exit(1);
1638     }
1639
1640   queue_init(&checkq);
1641   newpkgs = transaction_installedresult(trans, &checkq);
1642   newpkgsfps = 0;
1643
1644   if (newpkgs)
1645     {
1646       printf("Downloading %d packages\n", newpkgs);
1647       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
1648       for (i = 0; i < newpkgs; i++)
1649         {
1650           unsigned int medianr;
1651           char *loc;
1652           Solvable *s;
1653           struct repoinfo *cinfo;
1654           const unsigned char *chksum;
1655           Id chksumtype;
1656           Dataiterator di;
1657
1658           p = checkq.elements[i];
1659           s = pool_id2solvable(pool, p);
1660           cinfo = s->repo->appdata;
1661           if (!cinfo)
1662             {
1663               printf("%s: no repository information\n", s->repo->name);
1664               exit(1);
1665             }
1666           loc = solvable_get_location(s, &medianr);
1667           if (!loc)
1668              continue;
1669
1670           if (pool->installed && pool->installed->nsolvables)
1671             {
1672               /* try a delta first */
1673               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, id2str(pool, s->name), SEARCH_STRING);
1674               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
1675               while (dataiterator_step(&di))
1676                 {
1677                   Id baseevr, op;
1678
1679                   dataiterator_setpos_parent(&di);
1680                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
1681                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
1682                     continue;
1683                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
1684                   FOR_PROVIDES(op, pp, s->name)
1685                     {
1686                       Solvable *os = pool->solvables + op;
1687                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
1688                         break;
1689                     }
1690                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
1691                     {
1692                       /* base is installed, run sequence check */
1693                       const char *seqname;
1694                       const char *seqevr;
1695                       const char *seqnum;
1696                       const char *seq;
1697                       const char *dloc;
1698                       FILE *fp;
1699                       char cmd[128];
1700                       int newfd;
1701
1702                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
1703                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
1704                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
1705                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
1706                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
1707                       if (system(pool_tmpjoin(pool, "/usr/bin/applydeltarpm -c -s ", seq, 0)) != 0)
1708                         continue;       /* didn't match */
1709                       /* looks good, download delta */
1710                       chksumtype = 0;
1711                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
1712                       if (!chksumtype)
1713                         continue;       /* no way! */
1714                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
1715                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
1716                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
1717                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
1718                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype)) == 0)
1719                         continue;
1720                       /* got it, now reconstruct */
1721                       newfd = opentmpfile();
1722                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
1723                       fcntl(fileno(fp), F_SETFD, 0);
1724                       if (system(cmd))
1725                         {
1726                           close(newfd);
1727                           fclose(fp);
1728                           continue;
1729                         }
1730                       lseek(newfd, 0, SEEK_SET);
1731                       chksumtype = 0;
1732                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
1733                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
1734                         {
1735                           close(newfd);
1736                           fclose(fp);
1737                           continue;
1738                         }
1739                       newpkgsfps[i] = fdopen(newfd, "r");
1740                       fclose(fp);
1741                       break;
1742                     }
1743                 }
1744               dataiterator_free(&di);
1745             }
1746           
1747           if (newpkgsfps[i])
1748             {
1749               putchar('d');
1750               fflush(stdout);
1751               continue;         /* delta worked! */
1752             }
1753           if (cinfo->type == TYPE_SUSETAGS)
1754             {
1755               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
1756               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
1757             }
1758           chksumtype = 0;
1759           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
1760           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype)) == 0)
1761             {
1762               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
1763               exit(1);
1764             }
1765           putchar('.');
1766           fflush(stdout);
1767         }
1768       putchar('\n');
1769     }
1770
1771   if (newpkgs)
1772     {
1773       Queue conflicts;
1774
1775       printf("Searching for file conflicts\n");
1776       queue_init(&conflicts);
1777       fcstate.rpmdbstate = 0;
1778       fcstate.newpkgscnt = newpkgs;
1779       fcstate.checkq = &checkq;
1780       fcstate.newpkgsfps = newpkgsfps;
1781       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
1782       if (conflicts.count)
1783         {
1784           printf("\n");
1785           for (i = 0; i < conflicts.count; i += 5)
1786             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]));
1787           printf("\n");
1788           if (yesno("Re-run solver (y/n/q)? "))
1789             {
1790               for (i = 0; i < newpkgs; i++)
1791                 if (newpkgsfps[i])
1792                   fclose(newpkgsfps[i]);
1793               newpkgsfps = sat_free(newpkgsfps);
1794               solver_free(solv);
1795               pool_add_fileconflicts_deps(pool, &conflicts);
1796               pool_createwhatprovides(pool);    /* Hmm... */
1797               goto rerunsolver;
1798             }
1799         }
1800       queue_free(&conflicts);
1801     }
1802
1803   printf("Committing transaction:\n\n");
1804   transaction_order(trans, 0);
1805   for (i = 0; i < trans->steps.count; i++)
1806     {
1807       const char *evr, *evrp, *nvra;
1808       Solvable *s;
1809       int j;
1810       FILE *fp;
1811
1812       p = trans->steps.elements[i];
1813       s = pool_id2solvable(pool, p);
1814       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
1815       switch(type)
1816         {
1817         case SOLVER_TRANSACTION_ERASE:
1818           printf("erase %s\n", solvid2str(pool, p));
1819           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
1820             continue;
1821           /* strip epoch from evr */
1822           evr = evrp = id2str(pool, s->evr);
1823           while (*evrp >= '0' && *evrp <= '9')
1824             evrp++;
1825           if (evrp > evr && evrp[0] == ':' && evrp[1])
1826             evr = evrp + 1;
1827           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
1828           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
1829           runrpm("-e", nvra, -1);       /* to bad that --querybynumber doesn't work */
1830           break;
1831         case SOLVER_TRANSACTION_INSTALL:
1832         case SOLVER_TRANSACTION_MULTIINSTALL:
1833           printf("install %s\n", solvid2str(pool, p));
1834           for (j = 0; j < newpkgs; j++)
1835             if (checkq.elements[j] == p)
1836               break;
1837           fp = j < newpkgs ? newpkgsfps[j] : 0;
1838           if (!fp)
1839             continue;
1840           rewind(fp);
1841           lseek(fileno(fp), 0, SEEK_SET);
1842           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
1843           fclose(fp);
1844           newpkgsfps[j] = 0;
1845           break;
1846         default:
1847           break;
1848         }
1849     }
1850
1851   for (i = 0; i < newpkgs; i++)
1852     if (newpkgsfps[i])
1853       fclose(newpkgsfps[i]);
1854   sat_free(newpkgsfps);
1855   queue_free(&checkq);
1856   solver_free(solv);
1857   queue_free(&job);
1858   pool_free(pool);
1859   exit(0);
1860 }