- clean up system type definition, use -DSUSE=1 when building for suse
[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 it does:
11  * - understands globs for package names / dependencies
12  * - understands .arch suffix
13  * - installation of commandline packages
14  * - repository data caching
15  * - on demand loading of secondary repository data
16  * - gpg and checksum verification
17  * - file conflicts
18  * - deltarpm support
19  * - fastestmirror implementation
20  *
21  * things available in the library but missing from solv:
22  * - vendor policy loading
23  * - soft locks file handling
24  * - multi version handling
25  */
26
27 #define _GNU_SOURCE
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <fnmatch.h>
33 #include <unistd.h>
34 #include <zlib.h>
35 #include <fcntl.h>
36 #include <assert.h>
37 #include <sys/utsname.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <time.h>
41 #include <sys/time.h>
42 #include <sys/dir.h>
43 #include <sys/stat.h>
44
45 #include <sys/socket.h>
46 #include <netdb.h>
47 #include <poll.h>
48 #include <errno.h>
49
50 #include "pool.h"
51 #include "poolarch.h"
52 #include "repo.h"
53 #include "evr.h"
54 #include "policy.h"
55 #include "util.h"
56 #include "solver.h"
57 #include "solverdebug.h"
58 #include "chksum.h"
59 #include "repo_solv.h"
60
61 #include "repo_write.h"
62 #ifdef ENABLE_RPMDB
63 #include "repo_rpmdb.h"
64 #include "pool_fileconflicts.h"
65 #endif
66 #ifdef ENABLE_DEBIAN
67 #include "repo_deb.h"
68 #endif
69 #ifdef ENABLE_RPMMD
70 #include "repo_rpmmd.h"
71 #include "repo_repomdxml.h"
72 #include "repo_updateinfoxml.h"
73 #include "repo_deltainfoxml.h"
74 #endif
75 #ifdef ENABLE_SUSEREPO
76 #include "repo_products.h"
77 #include "repo_susetags.h"
78 #include "repo_content.h"
79 #endif
80 #include "solv_xfopen.h"
81
82 #ifdef FEDORA
83 # define REPOINFO_PATH "/etc/yum.repos.d"
84 #endif
85 #ifdef SUSE
86 # define REPOINFO_PATH "/etc/zypp/repos.d"
87 # define PRODUCTS_PATH "/etc/products.d"
88 # define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
89 #endif
90
91 #define SOLVCACHE_PATH "/var/cache/solv"
92
93 #define METADATA_EXPIRE (60 * 15)
94
95 struct repoinfo {
96   Repo *repo;
97
98   char *alias;
99   char *name;
100   int enabled;
101   int autorefresh;
102   char *baseurl;
103   char *metalink;
104   char *mirrorlist;
105   char *path;
106   int type;
107   int pkgs_gpgcheck;
108   int repo_gpgcheck;
109   int priority;
110   int keeppackages;
111   int metadata_expire;
112   char **components;
113   int ncomponents;
114
115   unsigned char cookie[32];
116   unsigned char extcookie[32];
117 };
118
119 #ifdef FEDORA
120 char *
121 yum_substitute(Pool *pool, char *line)
122 {
123   char *p, *p2;
124   static char *releaseevr;
125   static char *basearch;
126
127   if (!line)
128     {
129       solv_free(releaseevr);
130       releaseevr = 0;
131       solv_free(basearch);
132       basearch = 0;
133       return 0;
134     }
135   p = line;
136   while ((p2 = strchr(p, '$')) != 0)
137     {
138       if (!strncmp(p2, "$releasever", 11))
139         {
140           if (!releaseevr)
141             {
142               Queue q;
143         
144               queue_init(&q);
145               rpm_installedrpmdbids(0, "Providename", "redhat-release", &q);
146               if (q.count)
147                 {
148                   void *handle, *state = 0;
149                   char *p;
150                   handle = rpm_byrpmdbid(q.elements[0], 0, &state);
151                   releaseevr = rpm_query(handle, SOLVABLE_EVR);
152                   rpm_byrpmdbid(0, 0, &state);
153                   if ((p = strchr(releaseevr, '-')) != 0)
154                     *p = 0;
155                 }
156               queue_free(&q);
157               if (!releaseevr)
158                 releaseevr = strdup("?");
159             }
160           *p2 = 0;
161           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
162           p2 = p + (p2 - line);
163           line = p;
164           p = p2 + strlen(releaseevr);
165           continue;
166         }
167       if (!strncmp(p2, "$basearch", 9))
168         {
169           if (!basearch)
170             {
171               struct utsname un;
172               if (uname(&un))
173                 {
174                   perror("uname");
175                   exit(1);
176                 }
177               basearch = strdup(un.machine);
178               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
179                 basearch[1] = '3';
180             }
181           *p2 = 0;
182           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
183           p2 = p + (p2 - line);
184           line = p;
185           p = p2 + strlen(basearch);
186           continue;
187         }
188       p = p2 + 1;
189     }
190   return line;
191 }
192 #endif
193
194 #define TYPE_UNKNOWN    0
195 #define TYPE_SUSETAGS   1
196 #define TYPE_RPMMD      2
197 #define TYPE_PLAINDIR   3
198 #define TYPE_DEBIAN     4
199
200 #ifndef NOSYSTEM
201 static int
202 read_repoinfos_sort(const void *ap, const void *bp)
203 {
204   const struct repoinfo *a = ap;
205   const struct repoinfo *b = bp;
206   return strcmp(a->alias, b->alias);
207 }
208 #endif
209
210 #if defined(SUSE) || defined(FEDORA)
211
212 struct repoinfo *
213 read_repoinfos(Pool *pool, int *nrepoinfosp)
214 {
215   const char *reposdir = REPOINFO_PATH;
216   char buf[4096];
217   char buf2[4096], *kp, *vp, *kpe;
218   DIR *dir;
219   FILE *fp;
220   struct dirent *ent;
221   int l, rdlen;
222   struct repoinfo *repoinfos = 0, *cinfo;
223   int nrepoinfos = 0;
224
225   rdlen = strlen(reposdir);
226   dir = opendir(reposdir);
227   if (!dir)
228     {
229       *nrepoinfosp = 0;
230       return 0;
231     }
232   while ((ent = readdir(dir)) != 0)
233     {
234       if (ent->d_name[0] == '.')
235         continue;
236       l = strlen(ent->d_name);
237       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
238         continue;
239       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
240       if ((fp = fopen(buf, "r")) == 0)
241         {
242           perror(buf);
243           continue;
244         }
245       cinfo = 0;
246       while(fgets(buf2, sizeof(buf2), fp))
247         {
248           l = strlen(buf2);
249           if (l == 0)
250             continue;
251           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
252             buf2[--l] = 0;
253           kp = buf2;
254           while (*kp == ' ' || *kp == '\t')
255             kp++;
256           if (!*kp || *kp == '#')
257             continue;
258 #ifdef FEDORA
259           if (strchr(kp, '$'))
260             kp = yum_substitute(pool, kp);
261 #endif
262           if (*kp == '[')
263             {
264               vp = strrchr(kp, ']');
265               if (!vp)
266                 continue;
267               *vp = 0;
268               repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
269               cinfo = repoinfos + nrepoinfos++;
270               memset(cinfo, 0, sizeof(*cinfo));
271               cinfo->alias = strdup(kp + 1);
272               cinfo->type = TYPE_RPMMD;
273               cinfo->autorefresh = 1;
274               cinfo->priority = 99;
275 #ifndef FEDORA
276               cinfo->repo_gpgcheck = 1;
277 #endif
278               cinfo->metadata_expire = METADATA_EXPIRE;
279               continue;
280             }
281           if (!cinfo)
282             continue;
283           vp = strchr(kp, '=');
284           if (!vp)
285             continue;
286           for (kpe = vp - 1; kpe >= kp; kpe--)
287             if (*kpe != ' ' && *kpe != '\t')
288               break;
289           if (kpe == kp)
290             continue;
291           vp++;
292           while (*vp == ' ' || *vp == '\t')
293             vp++;
294           kpe[1] = 0;
295           if (!strcmp(kp, "name"))
296             cinfo->name = strdup(vp);
297           else if (!strcmp(kp, "enabled"))
298             cinfo->enabled = *vp == '0' ? 0 : 1;
299           else if (!strcmp(kp, "autorefresh"))
300             cinfo->autorefresh = *vp == '0' ? 0 : 1;
301           else if (!strcmp(kp, "gpgcheck"))
302             cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
303           else if (!strcmp(kp, "repo_gpgcheck"))
304             cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
305           else if (!strcmp(kp, "baseurl"))
306             cinfo->baseurl = strdup(vp);
307           else if (!strcmp(kp, "mirrorlist"))
308             {
309               if (strstr(vp, "metalink"))
310                 cinfo->metalink = strdup(vp);
311               else
312                 cinfo->mirrorlist = strdup(vp);
313             }
314           else if (!strcmp(kp, "path"))
315             {
316               if (vp && strcmp(vp, "/") != 0)
317                 cinfo->path = strdup(vp);
318             }
319           else if (!strcmp(kp, "type"))
320             {
321               if (!strcmp(vp, "yast2"))
322                 cinfo->type = TYPE_SUSETAGS;
323               else if (!strcmp(vp, "rpm-md"))
324                 cinfo->type = TYPE_RPMMD;
325               else if (!strcmp(vp, "plaindir"))
326                 cinfo->type = TYPE_PLAINDIR;
327               else
328                 cinfo->type = TYPE_UNKNOWN;
329             }
330           else if (!strcmp(kp, "priority"))
331             cinfo->priority = atoi(vp);
332           else if (!strcmp(kp, "keeppackages"))
333             cinfo->keeppackages = *vp == '0' ? 0 : 1;
334         }
335       fclose(fp);
336       cinfo = 0;
337     }
338   closedir(dir);
339   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
340   *nrepoinfosp = nrepoinfos;
341   return repoinfos;
342 }
343
344 #endif
345
346 #ifdef DEBIAN
347
348 struct repoinfo *
349 read_repoinfos(Pool *pool, int *nrepoinfosp)
350 {
351   FILE *fp;
352   char buf[4096];
353   char buf2[4096];
354   int l;
355   char *kp, *url, *distro;
356   struct repoinfo *repoinfos = 0, *cinfo;
357   int nrepoinfos = 0;
358   DIR *dir = 0;
359   struct dirent *ent;
360
361   fp = fopen("/etc/apt/sources.list", "r");
362   while (1)
363     {
364       if (!fp)
365         {
366           if (!dir)
367             {
368               dir = opendir("/etc/apt/sources.list.d");
369               if (!dir)
370                 break;
371             }
372           if ((ent = readdir(dir)) == 0)
373             {
374               closedir(dir);
375               break;
376             }
377           if (ent->d_name[0] == '.')
378             continue;
379           l = strlen(ent->d_name);
380           if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
381             continue;
382           snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
383           if (!(fp = fopen(buf, "r")))
384             continue;
385         }
386       while(fgets(buf2, sizeof(buf2), fp))
387         {
388           l = strlen(buf2);
389           if (l == 0)
390             continue;
391           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
392             buf2[--l] = 0;
393           kp = buf2;
394           while (*kp == ' ' || *kp == '\t')
395             kp++;
396           if (!*kp || *kp == '#')
397             continue;
398           if (strncmp(kp, "deb", 3) != 0)
399             continue;
400           kp += 3;
401           if (*kp != ' ' && *kp != '\t')
402             continue;
403           while (*kp == ' ' || *kp == '\t')
404             kp++;
405           if (!*kp)
406             continue;
407           url = kp;
408           while (*kp && *kp != ' ' && *kp != '\t')
409             kp++;
410           if (*kp)
411             *kp++ = 0;
412           while (*kp == ' ' || *kp == '\t')
413             kp++;
414           if (!*kp)
415             continue;
416           distro = kp;
417           while (*kp && *kp != ' ' && *kp != '\t')
418             kp++;
419           if (*kp)
420             *kp++ = 0;
421           while (*kp == ' ' || *kp == '\t')
422             kp++;
423           if (!*kp)
424             continue;
425           repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
426           cinfo = repoinfos + nrepoinfos++;
427           memset(cinfo, 0, sizeof(*cinfo));
428           cinfo->baseurl = strdup(url);
429           cinfo->alias = solv_dupjoin(url, "/", distro);
430           cinfo->name = strdup(distro);
431           cinfo->type = TYPE_DEBIAN;
432           cinfo->enabled = 1;
433           cinfo->autorefresh = 1;
434           cinfo->repo_gpgcheck = 1;
435           cinfo->metadata_expire = METADATA_EXPIRE;
436           while (*kp)
437             {
438               char *compo;
439               while (*kp == ' ' || *kp == '\t')
440                 kp++;
441               if (!*kp)
442                 break;
443               compo = kp;
444               while (*kp && *kp != ' ' && *kp != '\t')
445                 kp++;
446               if (*kp)
447                 *kp++ = 0;
448               cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
449               cinfo->components[cinfo->ncomponents++] = strdup(compo);
450             }
451         }
452       fclose(fp);
453       fp = 0;
454     }
455   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
456   *nrepoinfosp = nrepoinfos;
457   return repoinfos;
458 }
459
460 #endif
461
462 #ifdef NOSYSTEM
463 struct repoinfo *
464 read_repoinfos(Pool *pool, int *nrepoinfosp)
465 {
466   *nrepoinfosp = 0;
467   return 0;
468 }
469 #endif
470
471
472 void
473 free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
474 {
475   int i, j;
476   for (i = 0; i < nrepoinfos; i++)
477     {
478       struct repoinfo *cinfo = repoinfos + i;
479       solv_free(cinfo->name);
480       solv_free(cinfo->alias);
481       solv_free(cinfo->path);
482       solv_free(cinfo->metalink);
483       solv_free(cinfo->mirrorlist);
484       solv_free(cinfo->baseurl);
485       for (j = 0; j < cinfo->ncomponents; j++)
486         solv_free(cinfo->components[j]);
487       solv_free(cinfo->components);
488     }
489   solv_free(repoinfos);
490 }
491
492 static inline int
493 opentmpfile()
494 {
495   char tmpl[100];
496   int fd;
497
498   strcpy(tmpl, "/var/tmp/solvXXXXXX");
499   fd = mkstemp(tmpl);
500   if (fd < 0)
501     {
502       perror("mkstemp");
503       exit(1);
504     }
505   unlink(tmpl);
506   return fd;
507 }
508
509 static int
510 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
511 {
512   char buf[1024];
513   const unsigned char *sum;
514   void *h;
515   int l;
516
517   h = solv_chksum_create(chksumtype);
518   if (!h)
519     {
520       printf("%s: unknown checksum type\n", file);
521       return 0;
522     }
523   while ((l = read(fd, buf, sizeof(buf))) > 0)
524     solv_chksum_add(h, buf, l);
525   lseek(fd, 0, SEEK_SET);
526   l = 0;
527   sum = solv_chksum_get(h, &l);
528   if (memcmp(sum, chksum, l))
529     {
530       printf("%s: checksum mismatch\n", file);
531       solv_chksum_free(h, 0);
532       return 0;
533     }
534   solv_chksum_free(h, 0);
535   return 1;
536 }
537
538 void
539 findfastest(char **urls, int nurls)
540 {
541   int i, j, port;
542   int *socks, qc;
543   struct pollfd *fds;
544   char *p, *p2, *q;
545   char portstr[16];
546   struct addrinfo hints, *result;;
547
548   fds = solv_calloc(nurls, sizeof(*fds));
549   socks = solv_calloc(nurls, sizeof(*socks));
550   for (i = 0; i < nurls; i++)
551     {
552       socks[i] = -1;
553       p = strchr(urls[i], '/');
554       if (!p)
555         continue;
556       if (p[1] != '/')
557         continue;
558       p += 2;
559       q = strchr(p, '/');
560       qc = 0;
561       if (q)
562         {
563           qc = *q;
564           *q = 0;
565         }
566       if ((p2 = strchr(p, '@')) != 0)
567         p = p2 + 1;
568       port = 80;
569       if (!strncmp("https:", urls[i], 6))
570         port = 443;
571       else if (!strncmp("ftp:", urls[i], 4))
572         port = 21;
573       if ((p2 = strrchr(p, ':')) != 0)
574         {
575           port = atoi(p2 + 1);
576           if (q)
577             *q = qc;
578           q = p2;
579           qc = *q;
580           *q = 0;
581         }
582       sprintf(portstr, "%d", port);
583       memset(&hints, 0, sizeof(struct addrinfo));
584       hints.ai_family = AF_UNSPEC;
585       hints.ai_socktype = SOCK_STREAM;
586       hints.ai_flags = AI_NUMERICSERV;
587       result = 0;
588       if (!getaddrinfo(p, portstr, &hints, &result))
589         {
590           socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
591           if (socks[i] >= 0)
592             {
593               fcntl(socks[i], F_SETFL, O_NONBLOCK);
594               if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
595                 {
596                   if (errno != EINPROGRESS)
597                     {
598                       close(socks[i]);
599                       socks[i] = -1;
600                     }
601                 }
602             }
603           freeaddrinfo(result);
604         }
605       if (q)
606         *q = qc;
607     }
608   for (;;)
609     {
610       for (i = j = 0; i < nurls; i++)
611         {
612           if (socks[i] < 0)
613             continue;
614           fds[j].fd = socks[i];
615           fds[j].events = POLLOUT;
616           j++;
617         }
618       if (j < 2)
619         {
620           i = j - 1;
621           break;
622         }
623       if (poll(fds, j, 10000) <= 0)
624         {
625           i = -1;       /* something is wrong */
626           break;
627         }
628       for (i = 0; i < j; i++)
629         if ((fds[i].revents & POLLOUT) != 0)
630           {
631             int soe = 0;
632             socklen_t soel = sizeof(int);
633             if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
634               {
635                 /* connect failed, kill socket */
636                 for (j = 0; j < nurls; j++)
637                   if (socks[j] == fds[i].fd)
638                     {
639                       close(socks[j]);
640                       socks[j] = -1;
641                     }
642                 i = j + 1;
643                 break;
644               }
645             break;      /* horray! */
646           }
647       if (i == j + 1)
648         continue;
649       if (i == j)
650         i = -1;         /* something is wrong, no bit was set */
651       break;
652     }
653   /* now i contains the fastest fd index */
654   if (i >= 0)
655     {
656       for (j = 0; j < nurls; j++)
657         if (socks[j] == fds[i].fd)
658           break;
659       if (j != 0)
660         {
661           char *url0 = urls[0];
662           urls[0] = urls[j];
663           urls[j] = url0;
664         }
665     }
666   for (i = j = 0; i < nurls; i++)
667     if (socks[i] >= 0)
668       close(socks[i]);
669   free(socks);
670   free(fds);
671 }
672
673 char *
674 findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
675 {
676   char buf[4096], *bp, *ep;
677   char **urls = 0;
678   int nurls = 0;
679   int i;
680
681   if (chksumtypep)
682     *chksumtypep = 0;
683   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
684     {
685       while (*bp == ' ' || *bp == '\t')
686         bp++;
687       if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
688         {
689           bp += 20;
690           if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
691             *chksumtypep = REPOKEY_TYPE_SHA256;
692           continue;
693         }
694       if (strncmp(bp, "<url", 4))
695         continue;
696       bp = strchr(bp, '>');
697       if (!bp)
698         continue;
699       bp++;
700       ep = strstr(bp, "repodata/repomd.xml</url>");
701       if (!ep)
702         continue;
703       *ep = 0;
704       if (strncmp(bp, "http", 4))
705         continue;
706       urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
707       urls[nurls++] = strdup(bp);
708     }
709   if (nurls)
710     {
711       if (nurls > 1)
712         findfastest(urls, nurls > 5 ? 5 : nurls);
713       bp = urls[0];
714       urls[0] = 0;
715       for (i = 0; i < nurls; i++)
716         solv_free(urls[i]);
717       solv_free(urls);
718       ep = strchr(bp, '/');
719       if ((ep = strchr(ep + 2, '/')) != 0)
720         {
721           *ep = 0;
722           printf("[using mirror %s]\n", bp);
723           *ep = '/';
724         }
725       return bp;
726     }
727   return 0;
728 }
729
730 char *
731 findmirrorlisturl(FILE *fp)
732 {
733   char buf[4096], *bp, *ep;
734   int i, l;
735   char **urls = 0;
736   int nurls = 0;
737
738   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
739     {
740       while (*bp == ' ' || *bp == '\t')
741         bp++;
742       if (!*bp || *bp == '#')
743         continue;
744       l = strlen(bp);
745       while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
746         bp[--l] = 0;
747       urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
748       urls[nurls++] = strdup(bp);
749     }
750   if (nurls)
751     {
752       if (nurls > 1)
753         findfastest(urls, nurls > 5 ? 5 : nurls);
754       bp = urls[0];
755       urls[0] = 0;
756       for (i = 0; i < nurls; i++)
757         solv_free(urls[i]);
758       solv_free(urls);
759       ep = strchr(bp, '/');
760       if ((ep = strchr(ep + 2, '/')) != 0)
761         {
762           *ep = 0;
763           printf("[using mirror %s]\n", bp);
764           *ep = '/';
765         }
766       return bp;
767     }
768   return 0;
769 }
770
771 static inline int
772 iscompressed(const char *name)
773 {
774   int l = strlen(name);
775   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
776 }
777
778 FILE *
779 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int *badchecksump)
780 {
781   pid_t pid;
782   int fd, l;
783   int status;
784   char url[4096];
785   const char *baseurl = cinfo->baseurl;
786
787   if (!baseurl)
788     {
789       if (!cinfo->metalink && !cinfo->mirrorlist)
790         return 0;
791       if (file != cinfo->metalink && file != cinfo->mirrorlist)
792         {
793           FILE *fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
794           unsigned char mlchksum[32];
795           Id mlchksumtype = 0;
796           if (!fp)
797             return 0;
798           if (cinfo->metalink)
799             cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
800           else
801             cinfo->baseurl = findmirrorlisturl(fp);
802           fclose(fp);
803           if (!cinfo->baseurl)
804             return 0;
805 #ifdef FEDORA
806           if (strchr(cinfo->baseurl, '$'))
807             {
808               char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
809               free(cinfo->baseurl);
810               cinfo->baseurl = strdup(b);
811             }
812 #endif
813           if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
814             {
815               chksumtype = mlchksumtype;
816               chksum = mlchksum;
817             }
818           return curlfopen(cinfo, file, uncompress, chksum, chksumtype, badchecksump);
819         }
820       snprintf(url, sizeof(url), "%s", file);
821     }
822   else
823     {
824       l = strlen(baseurl);
825       if (l && baseurl[l - 1] == '/')
826         snprintf(url, sizeof(url), "%s%s", baseurl, file);
827       else
828         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
829     }
830   fd = opentmpfile();
831   // printf("url: %s\n", url);
832   if ((pid = fork()) == (pid_t)-1)
833     {
834       perror("fork");
835       exit(1);
836     }
837   if (pid == 0)
838     {
839       if (fd != 1)
840         {
841           dup2(fd, 1);
842           close(fd);
843         }
844       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
845       perror("curl");
846       _exit(0);
847     }
848   status = 0;
849   while (waitpid(pid, &status, 0) != pid)
850     ;
851   if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
852     {
853       /* empty file */
854       close(fd);
855       return 0;
856     }
857   lseek(fd, 0, SEEK_SET);
858   if (status)
859     {
860       printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
861       if (badchecksump)
862         *badchecksump = 1;
863       close(fd);
864       return 0;
865     }
866   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
867     {
868       if (badchecksump)
869         *badchecksump = 1;
870       close(fd);
871       return 0;
872     }
873   if (uncompress)
874     return solv_xfopen_fd(".gz", fd, "r");
875   fcntl(fd, F_SETFD, FD_CLOEXEC);
876   return fdopen(fd, "r");
877 }
878
879 #ifndef DEBIAN
880
881 static void
882 cleanupgpg(char *gpgdir)
883 {
884   char cmd[256];
885   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
886   unlink(cmd);
887   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
888   unlink(cmd);
889   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
890   unlink(cmd);
891   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
892   unlink(cmd);
893   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
894   unlink(cmd);
895   rmdir(gpgdir);
896 }
897
898 int
899 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
900 {
901   char *gpgdir;
902   char *keysfile;
903   const char *pubkey;
904   char cmd[256];
905   FILE *kfp;
906   Solvable *s;
907   Id p;
908   off_t posfp, possigfp;
909   int r, nkeys;
910
911   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
912   if (!gpgdir)
913     return 0;
914   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
915   if (!(kfp = fopen(keysfile, "w")) )
916     {
917       cleanupgpg(gpgdir);
918       return 0;
919     }
920   nkeys = 0;
921   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
922     {
923       if (!s->repo)
924         continue;
925       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
926       if (!pubkey || !*pubkey)
927         continue;
928       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
929         break;
930       if (fputc('\n', kfp) == EOF)      /* Just in case... */
931         break;
932       nkeys++;
933     }
934   if (fclose(kfp) || !nkeys)
935     {
936       cleanupgpg(gpgdir);
937       return 0;
938     }
939   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
940   if (system(cmd))
941     {
942       fprintf(stderr, "key import error\n");
943       cleanupgpg(gpgdir);
944       return 0;
945     }
946   unlink(keysfile);
947   posfp = lseek(fileno(fp), 0, SEEK_CUR);
948   lseek(fileno(fp), 0, SEEK_SET);
949   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
950   lseek(fileno(sigfp), 0, SEEK_SET);
951   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
952   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
953   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
954   r = system(cmd);
955   lseek(fileno(sigfp), possigfp, SEEK_SET);
956   lseek(fileno(fp), posfp, SEEK_SET);
957   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
958   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
959   cleanupgpg(gpgdir);
960   return r == 0 ? 1 : 0;
961 }
962
963 #else
964
965 int
966 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
967 {
968   char cmd[256];
969   int r;
970
971   snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
972   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
973   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
974   r = system(cmd);
975   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
976   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
977   return r == 0 ? 1 : 0;
978 }
979
980 #endif
981
982 #define CHKSUM_IDENT "1.1"
983
984 void
985 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
986 {
987   char buf[4096];
988   void *h = solv_chksum_create(chktype);
989   int l;
990
991   solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
992   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
993     solv_chksum_add(h, buf, l);
994   rewind(fp);
995   solv_chksum_free(h, out);
996 }
997
998 void
999 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
1000 {
1001   void *h = solv_chksum_create(chktype);
1002   solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
1003   solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
1004   solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
1005   solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
1006   solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
1007   solv_chksum_free(h, out);
1008 }
1009
1010 void
1011 setarch(Pool *pool)
1012 {
1013   struct utsname un;
1014   if (uname(&un))
1015     {
1016       perror("uname");
1017       exit(1);
1018     }
1019   pool_setarch(pool, un.machine);
1020 }
1021
1022 char *calccachepath(Repo *repo, const char *repoext)
1023 {
1024   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
1025   if (repoext)
1026     {
1027       p = pool_tmpappend(repo->pool, p, "_", repoext);
1028       p = pool_tmpappend(repo->pool, p, ".solvx", 0);
1029     }
1030   else
1031     p = pool_tmpappend(repo->pool, p, ".solv", 0);
1032   q = p + strlen(SOLVCACHE_PATH) + 1;
1033   if (*q == '.')
1034     *q = '_';
1035   for (; *q; q++)
1036     if (*q == '/')
1037       *q = '_';
1038   return p;
1039 }
1040
1041 int
1042 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
1043 {
1044   FILE *fp;
1045   unsigned char mycookie[32];
1046   unsigned char myextcookie[32];
1047   struct repoinfo *cinfo;
1048   int flags;
1049
1050   cinfo = repo->appdata;
1051   if (!(fp = fopen(calccachepath(repo, repoext), "r")))
1052     return 0;
1053   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
1054     {
1055       fclose(fp);
1056       return 0;
1057     }
1058   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
1059     {
1060       fclose(fp);
1061       return 0;
1062     }
1063   if (cinfo && !repoext)
1064     {
1065       if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
1066         {
1067           fclose(fp);
1068           return 0;
1069         }
1070     }
1071   rewind(fp);
1072
1073   flags = 0;
1074   if (repoext)
1075     {
1076       flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
1077       if (strcmp(repoext, "DL") != 0)
1078         flags |= REPO_LOCALPOOL;        /* no local pool for DL so that we can compare IDs */
1079     }
1080
1081   if (repo_add_solv(repo, fp, flags))
1082     {
1083       fclose(fp);
1084       return 0;
1085     }
1086   if (cinfo && !repoext)
1087     {
1088       memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
1089       memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
1090     }
1091   if (mark)
1092     futimes(fileno(fp), 0);     /* try to set modification time */
1093   fclose(fp);
1094   return 1;
1095 }
1096
1097 void
1098 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
1099 {
1100   FILE *fp;
1101   int i, fd;
1102   char *tmpl;
1103   struct repoinfo *cinfo;
1104   int onepiece;
1105
1106   cinfo = repo->appdata;
1107   mkdir(SOLVCACHE_PATH, 0755);
1108   /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
1109   tmpl = solv_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
1110   fd = mkstemp(tmpl);
1111   if (fd < 0)
1112     {
1113       free(tmpl);
1114       return;
1115     }
1116   fchmod(fd, 0444);
1117   if (!(fp = fdopen(fd, "w")))
1118     {
1119       close(fd);
1120       unlink(tmpl);
1121       free(tmpl);
1122       return;
1123     }
1124
1125   onepiece = 1;
1126   for (i = repo->start; i < repo->end; i++)
1127    if (repo->pool->solvables[i].repo != repo)
1128      break;
1129   if (i < repo->end)
1130     onepiece = 0;
1131
1132   if (!info)
1133     repo_write(repo, fp);
1134   else if (repoext)
1135     repodata_write(info, fp);
1136   else
1137     {
1138       int oldnrepodata = repo->nrepodata;
1139       repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;    /* XXX: do this right */
1140       repo_write(repo, fp);
1141       repo->nrepodata = oldnrepodata;
1142       onepiece = 0;
1143     }
1144
1145   if (!repoext && cinfo)
1146     {
1147       if (!cinfo->extcookie[0])
1148         {
1149           /* create the ext cookie and append it */
1150           /* we just need some unique ID */
1151           struct stat stb;
1152           if (!fstat(fileno(fp), &stb))
1153             {
1154               int i;
1155
1156               calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
1157               for (i = 0; i < 32; i++)
1158                 cinfo->extcookie[i] ^= cookie[i];
1159             }
1160           if (cinfo->extcookie[0] == 0)
1161             cinfo->extcookie[0] = 1;
1162         }
1163       if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
1164         {
1165           fclose(fp);
1166           unlink(tmpl);
1167           free(tmpl);
1168           return;
1169         }
1170     }
1171   /* append our cookie describing the metadata state */
1172   if (fwrite(cookie, 32, 1, fp) != 1)
1173     {
1174       fclose(fp);
1175       unlink(tmpl);
1176       free(tmpl);
1177       return;
1178     }
1179   if (fclose(fp))
1180     {
1181       unlink(tmpl);
1182       free(tmpl);
1183       return;
1184     }
1185   if (onepiece)
1186     {
1187       /* switch to just saved repo to activate paging and save memory */
1188       FILE *fp = fopen(tmpl, "r");
1189       if (fp)
1190         {
1191           if (!repoext)
1192             {
1193               /* main repo */
1194               repo_empty(repo, 1);
1195               if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
1196                 {
1197                   /* oops, no way to recover from here */
1198                   fprintf(stderr, "internal error\n");
1199                   exit(1);
1200                 }
1201             }
1202           else
1203             {
1204               /* make sure repodata contains complete repo */
1205               /* (this is how repodata_write saves it) */
1206               repodata_extend_block(info, repo->start, repo->end - repo->start);
1207               info->state = REPODATA_LOADING;
1208               /* no need for LOCALPOOL as pool already contains ids */
1209               repo_add_solv(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1210               info->state = REPODATA_AVAILABLE; /* in case the load failed */
1211             }
1212           fclose(fp);
1213         }
1214     }
1215   if (!rename(tmpl, calccachepath(repo, repoext)))
1216     unlink(tmpl);
1217   free(tmpl);
1218 }
1219
1220
1221 static Pool *
1222 read_sigs()
1223 {
1224   Pool *sigpool = pool_create();
1225 #if defined(ENABLE_RPMDB_PUBKEY)
1226   Repo *repo = repo_create(sigpool, "rpmdbkeys");
1227   repo_add_rpmdb_pubkeys(repo, 0, 0);
1228 #endif
1229   return sigpool;
1230 }
1231
1232
1233 #ifdef ENABLE_RPMMD
1234 /* repomd helpers */
1235
1236 static inline const char *
1237 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1238 {
1239   Pool *pool = repo->pool;
1240   Dataiterator di;
1241   const char *filename;
1242
1243   filename = 0;
1244   *chksump = 0;
1245   *chksumtypep = 0;
1246   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1247   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1248   if (dataiterator_step(&di))
1249     {
1250       dataiterator_setpos_parent(&di);
1251       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1252       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
1253     }
1254   dataiterator_free(&di);
1255   if (filename && !*chksumtypep)
1256     {
1257       printf("no %s file checksum!\n", what);
1258       filename = 0;
1259     }
1260   return filename;
1261 }
1262
1263 int
1264 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
1265 {
1266   Id chksumtype, handle;
1267   const unsigned char *chksum;
1268   const char *filename;
1269
1270   filename = repomd_find(repo, what, &chksum, &chksumtype);
1271   if (!filename)
1272     return 0;
1273   if (!strcmp(what, "prestodelta"))
1274     what = "deltainfo";
1275   handle = repodata_new_handle(data);
1276   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1277   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1278   repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1279   if (!strcmp(what, "deltainfo"))
1280     {
1281       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1282       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1283     }
1284   if (!strcmp(what, "filelists"))
1285     {
1286       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1287       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1288     }
1289   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1290   return 1;
1291 }
1292
1293 int
1294 repomd_load_ext(Repo *repo, Repodata *data)
1295 {
1296   const char *filename, *repomdtype;
1297   char ext[3];
1298   FILE *fp;
1299   struct repoinfo *cinfo;
1300   const unsigned char *filechksum;
1301   Id filechksumtype;
1302
1303   cinfo = repo->appdata;
1304   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1305   if (!repomdtype)
1306     return 0;
1307   if (!strcmp(repomdtype, "filelists"))
1308     strcpy(ext, "FL");
1309   else if (!strcmp(repomdtype, "deltainfo"))
1310     strcpy(ext, "DL");
1311   else
1312     return 0;
1313 #if 1
1314   printf("[%s:%s", repo->name, ext);
1315 #endif
1316   if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
1317     {
1318       printf(" cached]\n");fflush(stdout);
1319       return 1;
1320     }
1321   printf(" fetching]\n"); fflush(stdout);
1322   filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1323   filechksumtype = 0;
1324   filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1325   if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1326     return 0;
1327   if (!strcmp(ext, "FL"))
1328     repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1329   else if (!strcmp(ext, "DL"))
1330     repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
1331   fclose(fp);
1332   writecachedrepo(repo, data, ext, cinfo->extcookie);
1333   return 1;
1334 }
1335
1336 #endif
1337
1338
1339 #ifdef ENABLE_SUSEREPO
1340 /* susetags helpers */
1341
1342 static inline const char *
1343 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1344 {
1345   Pool *pool = repo->pool;
1346   Dataiterator di;
1347   const char *filename;
1348
1349   filename = 0;
1350   *chksump = 0;
1351   *chksumtypep = 0;
1352   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1353   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1354   if (dataiterator_step(&di))
1355     {
1356       dataiterator_setpos_parent(&di);
1357       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1358       filename = what;
1359     }
1360   dataiterator_free(&di);
1361   if (filename && !*chksumtypep)
1362     {
1363       printf("no %s file checksum!\n", what);
1364       filename = 0;
1365     }
1366   return filename;
1367 }
1368
1369 static Id susetags_langtags[] = {
1370   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1371   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1372   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1373   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1374   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1375   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1376   0, 0
1377 };
1378
1379 void
1380 susetags_add_ext(Repo *repo, Repodata *data)
1381 {
1382   Pool *pool = repo->pool;
1383   Dataiterator di;
1384   char ext[3];
1385   Id handle, filechksumtype;
1386   const unsigned char *filechksum;
1387   int i;
1388
1389   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1390   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1391   while (dataiterator_step(&di))
1392     {
1393       if (strncmp(di.kv.str, "packages.", 9) != 0)
1394         continue;
1395       if (!strcmp(di.kv.str + 9, "gz"))
1396         continue;
1397       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1398         continue;
1399       ext[0] = di.kv.str[9];
1400       ext[1] = di.kv.str[10];
1401       ext[2] = 0;
1402       if (!strcmp(ext, "en"))
1403         continue;
1404       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1405         continue;
1406       handle = repodata_new_handle(data);
1407       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1408       if (filechksumtype)
1409         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1410       if (!strcmp(ext, "DU"))
1411         {
1412           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1413           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1414         }
1415       else if (!strcmp(ext, "FL"))
1416         {
1417           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1418           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1419         }
1420       else
1421         {
1422           for (i = 0; susetags_langtags[i]; i += 2)
1423             {
1424               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1425               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1426             }
1427         }
1428       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1429     }
1430   dataiterator_free(&di);
1431 }
1432
1433 int
1434 susetags_load_ext(Repo *repo, Repodata *data)
1435 {
1436   const char *filename, *descrdir;
1437   Id defvendor;
1438   char ext[3];
1439   FILE *fp;
1440   struct repoinfo *cinfo;
1441   const unsigned char *filechksum;
1442   Id filechksumtype;
1443
1444   cinfo = repo->appdata;
1445   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1446   if (!filename)
1447     return 0;
1448   /* susetags load */
1449   ext[0] = filename[9];
1450   ext[1] = filename[10];
1451   ext[2] = 0;
1452 #if 1
1453   printf("[%s:%s", repo->name, ext);
1454 #endif
1455   if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
1456     {
1457       printf(" cached]\n"); fflush(stdout);
1458       return 1;
1459     }
1460 #if 1
1461   printf(" fetching]\n"); fflush(stdout);
1462 #endif
1463   defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1464   descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1465   if (!descrdir)
1466     descrdir = "suse/setup/descr";
1467   filechksumtype = 0;
1468   filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1469   if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1470     return 0;
1471   repo_add_susetags(repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1472   fclose(fp);
1473   writecachedrepo(repo, data, ext, cinfo->extcookie);
1474   return 1;
1475 }
1476 #endif
1477
1478
1479
1480 /* load callback */
1481
1482 int
1483 load_stub(Pool *pool, Repodata *data, void *dp)
1484 {
1485   struct repoinfo *cinfo = data->repo->appdata;
1486 #ifdef ENABLE_SUSEREPO
1487   if (cinfo->type == TYPE_SUSETAGS)
1488     return susetags_load_ext(data->repo, data);
1489 #endif
1490 #ifdef ENABLE_RPMMD
1491   if (cinfo->type == TYPE_RPMMD)
1492     return repomd_load_ext(data->repo, data);
1493 #endif
1494   return 0;
1495 }
1496
1497 static unsigned char installedcookie[32];
1498
1499 #ifdef ENABLE_DEBIAN
1500
1501 const char *
1502 debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
1503 {
1504   char buf[4096];
1505   Id chksumtype;
1506   unsigned char *chksum;
1507   Id curchksumtype;
1508   int l, compl;
1509   char *ch, *fn, *bp;
1510   char *filename;
1511   static char *basearch;
1512   char *binarydir;
1513   int lbinarydir;
1514
1515   if (!basearch)
1516     {
1517       struct utsname un;
1518       if (uname(&un))
1519         {
1520           perror("uname");
1521           exit(1);
1522         }
1523       basearch = strdup(un.machine);
1524       if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
1525         basearch[1] = '3';
1526     }
1527   binarydir = solv_dupjoin("binary-", basearch, "/");
1528   lbinarydir = strlen(binarydir);
1529   compl = strlen(comp);
1530   rewind(fp);
1531   curchksumtype = 0;
1532   filename = 0;
1533   chksum = solv_malloc(32);
1534   chksumtype = 0;
1535   while(fgets(buf, sizeof(buf), fp))
1536     {
1537       l = strlen(buf);
1538       if (l == 0)
1539         continue;
1540       while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
1541         buf[--l] = 0;
1542       if (!strncasecmp(buf, "MD5Sum:", 7))
1543         {
1544           curchksumtype = REPOKEY_TYPE_MD5;
1545           continue;
1546         }
1547       if (!strncasecmp(buf, "SHA1:", 5))
1548         {
1549           curchksumtype = REPOKEY_TYPE_SHA1;
1550           continue;
1551         }
1552       if (!strncasecmp(buf, "SHA256:", 7))
1553         {
1554           curchksumtype = REPOKEY_TYPE_SHA256;
1555           continue;
1556         }
1557       if (!curchksumtype)
1558         continue;
1559       bp = buf;
1560       if (*bp++ != ' ')
1561         {
1562           curchksumtype = 0;
1563           continue;
1564         }
1565       ch = bp;
1566       while (*bp && *bp != ' ' && *bp != '\t')
1567         bp++;
1568       if (!*bp)
1569         continue;
1570       *bp++ = 0;
1571       while (*bp == ' ' || *bp == '\t')
1572         bp++;
1573       while (*bp && *bp != ' ' && *bp != '\t')
1574         bp++;
1575       if (!*bp)
1576         continue;
1577       while (*bp == ' ' || *bp == '\t')
1578         bp++;
1579       fn = bp;
1580       if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
1581         continue;
1582       bp += compl + 1;
1583       if (strncmp(bp, binarydir, lbinarydir))
1584         continue;
1585       bp += lbinarydir;
1586       if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
1587         {
1588           unsigned char curchksum[32];
1589           int curl;
1590           if (filename && !strcmp(bp, "Packages"))
1591             continue;
1592           curl = solv_chksum_len(curchksumtype);
1593           if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
1594             continue;
1595           if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
1596             continue;
1597           solv_free(filename);
1598           filename = strdup(fn);
1599           chksumtype = curchksumtype;
1600           memcpy(chksum, curchksum, curl);
1601         }
1602     }
1603   free(binarydir);
1604   if (filename)
1605     {
1606       fn = solv_dupjoin("/", filename, 0);
1607       solv_free(filename);
1608       filename = solv_dupjoin("dists/", cinfo->name, fn);
1609       solv_free(fn);
1610     }
1611   if (!chksumtype)
1612     chksum = solv_free(chksum);
1613   *chksump = chksum;
1614   *chksumtypep = chksumtype;
1615   return filename;
1616 }
1617 #endif
1618
1619 void
1620 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1621 {
1622   Repo *repo;
1623   struct repoinfo *cinfo;
1624   int i;
1625   FILE *fp;
1626   FILE *sigfp;
1627   const char *filename;
1628   const unsigned char *filechksum;
1629   Id filechksumtype;
1630 #ifdef ENABLE_SUSEREPO
1631   const char *descrdir;
1632   int defvendor;
1633 #endif
1634   struct stat stb;
1635   Pool *sigpool = 0;
1636   Repodata *data;
1637   int badchecksum;
1638   int dorefresh;
1639 #if defined(ENABLE_DEBIAN)
1640   FILE *fpr;
1641   int j;
1642 #endif
1643
1644   repo = repo_create(pool, "@System");
1645 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1646   printf("rpm database:");
1647   if (stat("/var/lib/rpm/Packages", &stb))
1648     memset(&stb, 0, sizeof(&stb));
1649 #endif
1650 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
1651   printf("dpgk database:");
1652   if (stat("/var/lib/dpkg/status", &stb))
1653     memset(&stb, 0, sizeof(&stb));
1654 #endif
1655 #ifdef NOSYSTEM
1656   printf("no installed database:");
1657   memset(&stb, 0, sizeof(&stb));
1658 #endif
1659   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, installedcookie);
1660   if (usecachedrepo(repo, 0, installedcookie, 0))
1661     printf(" cached\n");
1662   else
1663     {
1664 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1665       FILE *ofp;
1666       int done = 0;
1667 #endif
1668       printf(" reading\n");
1669
1670 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
1671 # if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
1672       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1673 # endif
1674       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1675         {
1676           Repo *ref = repo_create(pool, "@System.old");
1677           if (!repo_add_solv(ref, ofp, 0))
1678             {
1679               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1680               done = 1;
1681             }
1682           fclose(ofp);
1683           repo_free(ref, 1);
1684         }
1685       if (!done)
1686         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1687 #endif
1688 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
1689       repo_add_debdb(repo, 0, REPO_REUSE_REPODATA);
1690 #endif
1691       writecachedrepo(repo, 0, 0, installedcookie);
1692     }
1693   pool_set_installed(pool, repo);
1694
1695   for (i = 0; i < nrepoinfos; i++)
1696     {
1697       cinfo = repoinfos + i;
1698       if (!cinfo->enabled)
1699         continue;
1700
1701       repo = repo_create(pool, cinfo->alias);
1702       cinfo->repo = repo;
1703       repo->appdata = cinfo;
1704       repo->priority = 99 - cinfo->priority;
1705
1706       dorefresh = cinfo->autorefresh;
1707       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1708         {
1709           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1710             dorefresh = 0;
1711         }
1712       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1713         {
1714           printf("repo '%s':", cinfo->alias);
1715           printf(" cached\n");
1716           continue;
1717         }
1718       badchecksum = 0;
1719       switch (cinfo->type)
1720         {
1721 #ifdef ENABLE_RPMMD
1722         case TYPE_RPMMD:
1723           printf("rpmmd repo '%s':", cinfo->alias);
1724           fflush(stdout);
1725           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1726             {
1727               printf(" no repomd.xml file, skipped\n");
1728               repo_free(repo, 1);
1729               cinfo->repo = 0;
1730               break;
1731             }
1732           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1733           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1734             {
1735               printf(" cached\n");
1736               fclose(fp);
1737               break;
1738             }
1739           if (cinfo->repo_gpgcheck)
1740             {
1741               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1742               if (!sigfp)
1743                 {
1744                   printf(" unsigned, skipped\n");
1745                   fclose(fp);
1746                   break;
1747                 }
1748               if (!sigpool)
1749                 sigpool = read_sigs();
1750               if (!checksig(sigpool, fp, sigfp))
1751                 {
1752                   printf(" checksig failed, skipped\n");
1753                   fclose(sigfp);
1754                   fclose(fp);
1755                   break;
1756                 }
1757               fclose(sigfp);
1758             }
1759           repo_add_repomdxml(repo, fp, 0);
1760           fclose(fp);
1761           printf(" fetching\n");
1762           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1763           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1764             {
1765               repo_add_rpmmd(repo, fp, 0, 0);
1766               fclose(fp);
1767             }
1768           if (badchecksum)
1769             break;      /* hopeless */
1770
1771           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1772           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1773             {
1774               repo_add_updateinfoxml(repo, fp, 0);
1775               fclose(fp);
1776             }
1777
1778           data = repo_add_repodata(repo, 0);
1779           if (!repomd_add_ext(repo, data, "deltainfo"))
1780             repomd_add_ext(repo, data, "prestodelta");
1781           repomd_add_ext(repo, data, "filelists");
1782           repodata_internalize(data);
1783           if (!badchecksum)
1784             writecachedrepo(repo, 0, 0, cinfo->cookie);
1785           repodata_create_stubs(repo_last_repodata(repo));
1786           break;
1787 #endif
1788
1789 #ifdef ENABLE_SUSEREPO
1790         case TYPE_SUSETAGS:
1791           printf("susetags repo '%s':", cinfo->alias);
1792           fflush(stdout);
1793           descrdir = 0;
1794           defvendor = 0;
1795           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1796             {
1797               printf(" no content file, skipped\n");
1798               repo_free(repo, 1);
1799               cinfo->repo = 0;
1800               break;
1801             }
1802           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1803           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1804             {
1805               printf(" cached\n");
1806               fclose(fp);
1807               break;
1808             }
1809           if (cinfo->repo_gpgcheck)
1810             {
1811               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1812               if (!sigfp)
1813                 {
1814                   printf(" unsigned, skipped\n");
1815                   fclose(fp);
1816                   break;
1817                 }
1818               if (sigfp)
1819                 {
1820                   if (!sigpool)
1821                     sigpool = read_sigs();
1822                   if (!checksig(sigpool, fp, sigfp))
1823                     {
1824                       printf(" checksig failed, skipped\n");
1825                       fclose(sigfp);
1826                       fclose(fp);
1827                       break;
1828                     }
1829                   fclose(sigfp);
1830                 }
1831             }
1832           repo_add_content(repo, fp, 0);
1833           fclose(fp);
1834           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1835           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1836           if (!descrdir)
1837             descrdir = "suse/setup/descr";
1838           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1839           if (!filename)
1840             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1841           if (!filename)
1842             {
1843               printf(" no packages file entry, skipped\n");
1844               break;
1845             }
1846           printf(" fetching\n");
1847           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1848             break;      /* hopeless */
1849           repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES);
1850           fclose(fp);
1851           /* add default language */
1852           filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
1853           if (!filename)
1854             filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
1855           if (filename)
1856             {
1857               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1858                 {
1859                   repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES);
1860                   fclose(fp);
1861                 }
1862             }
1863           filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
1864           if (filename)
1865             {
1866               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1867                 {
1868                   char pbuf[256];
1869                   while (fgets(pbuf, sizeof(pbuf), fp))
1870                     {
1871                       int l = strlen(pbuf);
1872                       FILE *fp2;
1873                       if (l && pbuf[l - 1] == '\n')
1874                         pbuf[--l] = 0;
1875                       if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
1876                         continue;
1877                       filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
1878                       if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1879                         {
1880                           repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE);
1881                           fclose(fp2);
1882                         }
1883                     }
1884                   fclose(fp);
1885                 }
1886             }
1887           repo_internalize(repo);
1888           data = repo_add_repodata(repo, 0);
1889           susetags_add_ext(repo, data);
1890           repodata_internalize(data);
1891           if (!badchecksum)
1892             writecachedrepo(repo, 0, 0, cinfo->cookie);
1893           repodata_create_stubs(repo_last_repodata(repo));
1894           break;
1895 #endif
1896
1897 #if defined(ENABLE_DEBIAN)
1898         case TYPE_DEBIAN:
1899           printf("debian repo '%s':", cinfo->alias);
1900           fflush(stdout);
1901           filename = solv_dupjoin("dists/", cinfo->name, "/Release");
1902           if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
1903             {
1904               printf(" no Release file, skipped\n");
1905               repo_free(repo, 1);
1906               cinfo->repo = 0;
1907               free((char *)filename);
1908               break;
1909             }
1910           solv_free((char *)filename);
1911           if (cinfo->repo_gpgcheck)
1912             {
1913               filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
1914               sigfp = curlfopen(cinfo, filename, 0, 0, 0, 0);
1915               solv_free((char *)filename);
1916               if (!sigfp)
1917                 {
1918                   printf(" unsigned, skipped\n");
1919                   fclose(fpr);
1920                   break;
1921                 }
1922               if (!sigpool)
1923                 sigpool = read_sigs();
1924               if (!checksig(sigpool, fpr, sigfp))
1925                 {
1926                   printf(" checksig failed, skipped\n");
1927                   fclose(sigfp);
1928                   fclose(fpr);
1929                   break;
1930                 }
1931               fclose(sigfp);
1932             }
1933           calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
1934           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1935             {
1936               printf(" cached\n");
1937               fclose(fpr);
1938               break;
1939             }
1940           printf(" fetching\n");
1941           for (j = 0; j < cinfo->ncomponents; j++)
1942             {
1943               if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
1944                 {
1945                   printf("[component %s not found]\n", cinfo->components[j]);
1946                   continue;
1947                 }
1948               if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1949                 {
1950                   repo_add_debpackages(repo, fp, 0);
1951                   fclose(fp);
1952                 }
1953               solv_free((char *)filechksum);
1954               solv_free((char *)filename);
1955             }
1956           fclose(fpr);
1957           if (!badchecksum)
1958             writecachedrepo(repo, 0, 0, cinfo->cookie);
1959           break;
1960 #endif
1961
1962         default:
1963           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1964           repo_free(repo, 1);
1965           cinfo->repo = 0;
1966           break;
1967         }
1968     }
1969   if (sigpool)
1970     pool_free(sigpool);
1971 }
1972
1973
1974 int
1975 str2archid(Pool *pool, char *arch)
1976 {
1977   Id id;
1978   if (!*arch)
1979     return 0;
1980   id = pool_str2id(pool, arch, 0);
1981   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1982     return id;
1983   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1984     return 0;
1985   return id;
1986 }
1987
1988
1989 #define DEPGLOB_NAME     1
1990 #define DEPGLOB_DEP      2
1991 #define DEPGLOB_NAMEDEP  3
1992
1993 int
1994 depglob(Pool *pool, char *name, Queue *job, int what)
1995 {
1996   Id p, pp;
1997   Id id = pool_str2id(pool, name, 0);
1998   int i, match = 0;
1999
2000   if (id)
2001     {
2002       FOR_PROVIDES(p, pp, id)
2003         {
2004           Solvable *s = pool->solvables + p;
2005           match = 1;
2006           if (s->name == id && (what & DEPGLOB_NAME) != 0)
2007             {
2008               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
2009               return 1;
2010             }
2011         }
2012       if (match)
2013         {
2014           if (what == DEPGLOB_NAMEDEP)
2015             printf("[using capability match for '%s']\n", name);
2016           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
2017           return 1;
2018         }
2019     }
2020
2021   if (strpbrk(name, "[*?") == 0)
2022     return 0;
2023
2024   if ((what & DEPGLOB_NAME) != 0)
2025     {
2026       /* looks like a name glob. hard work. */
2027       for (p = 1; p < pool->nsolvables; p++)
2028         {
2029           Solvable *s = pool->solvables + p;
2030           if (!s->repo || !pool_installable(pool, s))
2031             continue;
2032           id = s->name;
2033           if (fnmatch(name, pool_id2str(pool, id), 0) == 0)
2034             {
2035               for (i = 0; i < job->count; i += 2)
2036                 if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
2037                   break;
2038               if (i == job->count)
2039                 queue_push2(job, SOLVER_SOLVABLE_NAME, id);
2040               match = 1;
2041             }
2042         }
2043       if (match)
2044         return 1;
2045     }
2046   if ((what & DEPGLOB_DEP))
2047     {
2048       /* looks like a dep glob. really hard work. */
2049       for (id = 1; id < pool->ss.nstrings; id++)
2050         {
2051           if (!pool->whatprovides[id])
2052             continue;
2053           if (fnmatch(name, pool_id2str(pool, id), 0) == 0)
2054             {
2055               if (!match && what == DEPGLOB_NAMEDEP)
2056                 printf("[using capability match for '%s']\n", name);
2057               for (i = 0; i < job->count; i += 2)
2058                 if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
2059                   break;
2060               if (i == job->count)
2061                 queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
2062               match = 1;
2063             }
2064         }
2065       if (match)
2066         return 1;
2067     }
2068   return 0;
2069 }
2070
2071 int
2072 limitrelation(Pool *pool, Queue *job, int flags, Id evr)
2073 {
2074   int i, j;
2075   Id p, pp;
2076   for (i = j = 0; i < job->count; i += 2)
2077     {
2078       Id select = job->elements[i] & SOLVER_SELECTMASK;
2079       if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES)
2080         {
2081           fprintf(stderr, "limitrelation only works on name/provides jobs\n");
2082           exit(1);
2083         }
2084       job->elements[i + 1] = pool_rel2id(pool, job->elements[i + 1], evr, flags, 1);
2085       if (flags == REL_ARCH)
2086         job->elements[i] |= SOLVER_SETARCH;
2087       if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && job->elements[i])
2088         {
2089 #ifndef DEBIAN
2090           const char *evrstr = pool_id2str(pool, evr);
2091           if (!strchr(evrstr, '-'))
2092             job->elements[i] |= SOLVER_SETEV;
2093           else
2094 #endif
2095             job->elements[i] |= SOLVER_SETEVR;
2096         }
2097       /* make sure we still have matches */
2098       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
2099         break;
2100       if (p)
2101         {
2102           job->elements[j] = job->elements[i];
2103           job->elements[j + 1] = job->elements[i + 1];
2104           j += 2;
2105         }
2106     }
2107   queue_truncate(job, j);
2108   return j / 2;
2109 }
2110
2111 int
2112 limitrelation_arch(Pool *pool, Queue *job, int flags, char *evr)
2113 {
2114   char *r;
2115   Id archid;
2116   if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
2117     {
2118       *r = 0;
2119       limitrelation(pool, job, REL_ARCH, archid);
2120       limitrelation(pool, job, flags, pool_str2id(pool, evr, 1));
2121       *r = '.';
2122     }
2123   else
2124     limitrelation(pool, job, flags, pool_str2id(pool, evr, 1));
2125   return job->count / 2;
2126 }
2127
2128 int
2129 limitrepo(Pool *pool, Id repofilter, Queue *job)
2130 {
2131   Queue mq;
2132   Id p, pp;
2133   int i, j;
2134   Solvable *s;
2135
2136   queue_init(&mq);
2137   for (i = j = 0; i < job->count; i += 2)
2138     {
2139       queue_empty(&mq);
2140       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
2141         {
2142           s = pool_id2solvable(pool, p);
2143           if (s->repo && s->repo->repoid == repofilter)
2144              queue_push(&mq, p);
2145         }
2146       if (mq.count)
2147         {
2148           /* here we assume that repo == vendor, so we also set SOLVER_SETVENDOR */
2149           Id flags = (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO;
2150           if ((job->elements[i] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_NAME)
2151             flags |= SOLVER_SETNAME;
2152           if (mq.count == 1)
2153             {
2154               job->elements[j] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET | flags;
2155               job->elements[j + 1] = mq.elements[0];
2156             }
2157           else
2158             {
2159               job->elements[j] = SOLVER_SOLVABLE_ONE_OF | flags;
2160               job->elements[j + 1] = pool_queuetowhatprovides(pool, &mq);
2161             }
2162           j += 2;
2163         }
2164     }
2165   queue_truncate(job, j);
2166   queue_free(&mq);
2167   return j / 2;
2168 }
2169
2170 int
2171 mkselect(Pool *pool, int mode, char *name, Queue *job)
2172 {
2173   char *r, *r2;
2174   Id archid;
2175
2176   if (*name == '/')
2177     {
2178       Dataiterator di;
2179       int type = strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
2180       Queue q;
2181       queue_init(&q);
2182       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
2183       while (dataiterator_step(&di))
2184         {
2185           Solvable *s = pool->solvables + di.solvid;
2186           if (!s->repo || !pool_installable(pool, s))
2187             continue;
2188           queue_push(&q, di.solvid);
2189           dataiterator_skip_solvable(&di);
2190         }
2191       dataiterator_free(&di);
2192       if (q.count)
2193         {
2194           printf("[using file list match for '%s']\n", name);
2195           if (q.count > 1)
2196             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
2197           else
2198             queue_push2(job, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
2199           queue_free(&q);
2200           return job->count / 2;
2201         }
2202     }
2203   if ((r = strpbrk(name, "<=>")) != 0)
2204     {
2205       /* relation case, support:
2206        * depglob rel
2207        * depglob.arch rel
2208        */
2209       int rflags = 0;
2210       int nend = r - name;
2211       char oldnend;
2212       for (; *r; r++)
2213         {
2214           if (*r == '<')
2215             rflags |= REL_LT;
2216           else if (*r == '=')
2217             rflags |= REL_EQ;
2218           else if (*r == '>')
2219             rflags |= REL_GT;
2220           else
2221             break;
2222         }
2223       while (*r && *r == ' ' && *r == '\t')
2224         r++;
2225       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
2226         nend--;
2227       if (!*name || !*r)
2228         {
2229           fprintf(stderr, "bad relation\n");
2230           exit(1);
2231         }
2232       oldnend = name[nend];
2233       name[nend] = 0;
2234       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2235         {
2236           name[nend] = oldnend;
2237           limitrelation(pool, job, rflags, pool_str2id(pool, r, 1));
2238           return job->count / 2;
2239         }
2240       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
2241         {
2242           *r2 = 0;
2243           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2244             {
2245               name[nend] = oldnend;
2246               *r2 = '.';
2247               limitrelation(pool, job, REL_ARCH, archid);
2248               limitrelation(pool, job, rflags, pool_str2id(pool, r, 1));
2249               return job->count / 2;
2250             }
2251           *r2 = '.';
2252         }
2253       name[nend] = oldnend;
2254     }
2255   else
2256     {
2257       /* no relation case, support:
2258        * depglob
2259        * depglob.arch
2260        * nameglob-version
2261        * nameglob-version.arch
2262        * nameglob-version-release
2263        * nameglob-version-release.arch
2264        */
2265       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2266         return job->count / 2;
2267       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
2268         {
2269           *r = 0;
2270           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2271             {
2272               *r = '.';
2273               limitrelation(pool, job, REL_ARCH, archid);
2274               return job->count / 2;
2275             }
2276           *r = '.';
2277         }
2278       if ((r = strrchr(name, '-')) != 0)
2279         {
2280           *r = 0;
2281           if (depglob(pool, name, job, DEPGLOB_NAME))
2282             {
2283               /* have just the version */
2284               limitrelation_arch(pool, job, REL_EQ, r + 1);
2285               *r = '-';
2286               return job->count / 2;
2287             }
2288           if ((r2 = strrchr(name, '-')) != 0)
2289             {
2290               *r = '-';
2291               *r2 = 0;
2292               r = r2;
2293               if (depglob(pool, name, job, DEPGLOB_NAME))
2294                 {
2295                   /* have version-release */
2296                   limitrelation_arch(pool, job, REL_EQ, r + 1);
2297                   *r = '-';
2298                   return job->count / 2;
2299                 }
2300             }
2301           *r = '-';
2302         }
2303     }
2304   return 0;
2305 }
2306
2307
2308 int
2309 yesno(const char *str)
2310 {
2311   char inbuf[128], *ip;
2312
2313   for (;;)
2314     {
2315       printf("%s", str);
2316       fflush(stdout);
2317       *inbuf = 0;
2318       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2319         {
2320           printf("Abort.\n");
2321           exit(1);
2322         }
2323       while (*ip == ' ' || *ip == '\t')
2324         ip++;
2325       if (*ip == 'q')
2326         {
2327           printf("Abort.\n");
2328           exit(1);
2329         }
2330       if (*ip == 'y' || *ip == 'n')
2331         return *ip == 'y' ? 1 : 0;
2332     }
2333 }
2334
2335 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2336
2337 struct fcstate {
2338   FILE **newpkgsfps;
2339   Queue *checkq;
2340   int newpkgscnt;
2341   void *rpmdbstate;
2342 };
2343
2344 static void *
2345 fileconflict_cb(Pool *pool, Id p, void *cbdata)
2346 {
2347   struct fcstate *fcstate = cbdata;
2348   Solvable *s;
2349   Id rpmdbid;
2350   int i;
2351   FILE *fp;
2352
2353   if (!p)
2354     {
2355       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
2356       return 0;
2357     }
2358   s = pool_id2solvable(pool, p);
2359   if (pool->installed && s->repo == pool->installed)
2360     {
2361       if (!s->repo->rpmdbid)
2362         return 0;
2363       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
2364       if (!rpmdbid)
2365         return 0;
2366        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
2367     }
2368   for (i = 0; i < fcstate->newpkgscnt; i++)
2369     if (fcstate->checkq->elements[i] == p)
2370       break;
2371   if (i == fcstate->newpkgscnt)
2372     return 0;
2373   fp = fcstate->newpkgsfps[i];
2374   if (!fp)
2375     return 0;
2376   rewind(fp);
2377   return rpm_byfp(fp, pool_solvable2str(pool, s), &fcstate->rpmdbstate);
2378 }
2379
2380
2381 void
2382 runrpm(const char *arg, const char *name, int dupfd3)
2383 {
2384   pid_t pid;
2385   int status;
2386
2387   if ((pid = fork()) == (pid_t)-1)
2388     {
2389       perror("fork");
2390       exit(1);
2391     }
2392   if (pid == 0)
2393     {
2394       if (dupfd3 != -1 && dupfd3 != 3)
2395         {
2396           dup2(dupfd3, 3);
2397           close(dupfd3);
2398         }
2399       if (dupfd3 != -1)
2400         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2401       if (strcmp(arg, "-e") == 0)
2402         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2403       else
2404         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2405       perror("rpm");
2406       _exit(0);
2407     }
2408   while (waitpid(pid, &status, 0) != pid)
2409     ;
2410   if (status)
2411     {
2412       printf("rpm failed\n");
2413       exit(1);
2414     }
2415 }
2416
2417 #endif
2418
2419 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2420
2421 void
2422 rundpkg(const char *arg, const char *name, int dupfd3)
2423 {
2424   pid_t pid;
2425   int status;
2426
2427   if ((pid = fork()) == (pid_t)-1)
2428     {
2429       perror("fork");
2430       exit(1);
2431     }
2432   if (pid == 0)
2433     {
2434       if (dupfd3 != -1 && dupfd3 != 3)
2435         {
2436           dup2(dupfd3, 3);
2437           close(dupfd3);
2438         }
2439       if (dupfd3 != -1)
2440         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2441       if (strcmp(arg, "--install") == 0)
2442         execlp("dpkg", "dpkg", "--install", "--force", "all", name, (char *)0);
2443       else
2444         execlp("dpkg", "dpkg", "--remove", "--force", "all", name, (char *)0);
2445       perror("dpkg");
2446       _exit(0);
2447     }
2448   while (waitpid(pid, &status, 0) != pid)
2449     ;
2450   if (status)
2451     {
2452       printf("dpkg failed\n");
2453       exit(1);
2454     }
2455 }
2456
2457 #endif
2458
2459 static Id
2460 nscallback(Pool *pool, void *data, Id name, Id evr)
2461 {
2462   if (name == NAMESPACE_PRODUCTBUDDY)
2463     {
2464       /* SUSE specific hack: each product has an associated rpm */
2465       Solvable *s = pool->solvables + evr;
2466       Id p, pp, cap;
2467       Id bestp = 0;
2468
2469       cap = pool_str2id(pool, pool_tmpjoin(pool, "product(", pool_id2str(pool, s->name) + 8, ")"), 0);
2470       if (!cap)
2471         return 0;
2472       cap = pool_rel2id(pool, cap, s->evr, REL_EQ, 0);
2473       if (!cap)
2474         return 0;
2475       FOR_PROVIDES(p, pp, cap)
2476         {
2477           Solvable *ps = pool->solvables + p;
2478           if (ps->repo == s->repo && ps->arch == s->arch)
2479             if (!bestp || pool_evrcmp(pool, pool->solvables[bestp].evr, ps->evr, EVRCMP_COMPARE) < 0)
2480               bestp = p;
2481         }
2482       return bestp;
2483     }
2484   return 0;
2485 }
2486
2487 #ifdef SOFTLOCKS_PATH
2488
2489 void
2490 addsoftlocks(Pool *pool, Queue *job)
2491 {
2492   FILE *fp;
2493   Id type, id, p, pp;
2494   char *bp, *ep, buf[4096];
2495
2496   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2497     return;
2498   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2499     {
2500       while (*bp == ' ' || *bp == '\t')
2501         bp++;
2502       if (!*bp || *bp == '#')
2503         continue;
2504       for (ep = bp; *ep; ep++)
2505         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2506           break;
2507       *ep = 0;
2508       type = SOLVER_SOLVABLE_NAME;
2509       if (!strncmp(bp, "provides:", 9) && bp[9])
2510         {
2511           type = SOLVER_SOLVABLE_PROVIDES;
2512           bp += 9;
2513         }
2514       id = pool_str2id(pool, bp, 1);
2515       if (pool->installed)
2516         {
2517           FOR_JOB_SELECT(p, pp, type, id)
2518             if (pool->solvables[p].repo == pool->installed)
2519               break;
2520           if (p)
2521             continue;   /* ignore, as it is already installed */
2522         }
2523       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2524     }
2525   fclose(fp);
2526 }
2527
2528 #endif
2529
2530
2531 void
2532 rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
2533 {
2534   Repo *repo;
2535   Repodata *data;
2536   Map providedids;
2537   Queue fileprovidesq;
2538   int i, j, n;
2539   struct repoinfo *cinfo;
2540
2541   map_init(&providedids, pool->ss.nstrings);
2542   queue_init(&fileprovidesq);
2543   for (i = 0; i < addedfileprovides->count; i++)
2544     MAPSET(&providedids, addedfileprovides->elements[i]);
2545   FOR_REPOS(i, repo)
2546     {
2547       /* make sure all repodatas but the first are extensions */
2548       if (repo->nrepodata < 2)
2549         continue;
2550       data = repo_id2repodata(repo, 1);
2551       if (data->loadcallback)
2552         continue;
2553       for (j = 2; j < repo->nrepodata; j++)
2554         {
2555           Repodata *edata = repo_id2repodata(repo, j);
2556           if (!edata->loadcallback)
2557             break;
2558         }
2559       if (j < repo->nrepodata)
2560         continue;       /* found a non-externsion repodata, can't rewrite  */
2561       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2562         {
2563           if (repo == pool->installed && addedfileprovides_inst)
2564             {
2565               for (j = 0; j < addedfileprovides->count; j++)
2566                 MAPCLR(&providedids, addedfileprovides->elements[j]);
2567               for (j = 0; j < addedfileprovides_inst->count; j++)
2568                 MAPSET(&providedids, addedfileprovides_inst->elements[j]);
2569             }
2570           n = 0;
2571           for (j = 0; j < fileprovidesq.count; j++)
2572             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2573               n++;
2574           if (repo == pool->installed && addedfileprovides_inst)
2575             {
2576               for (j = 0; j < addedfileprovides_inst->count; j++)
2577                 MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
2578               for (j = 0; j < addedfileprovides->count; j++)
2579                 MAPSET(&providedids, addedfileprovides->elements[j]);
2580               if (n == addedfileprovides_inst->count)
2581                 continue;       /* nothing new added */
2582             }
2583           else if (n == addedfileprovides->count)
2584             continue;   /* nothing new added */
2585         }
2586       repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
2587       repodata_internalize(data);
2588       cinfo = repo->appdata;
2589       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2590     }
2591   queue_free(&fileprovidesq);
2592   map_free(&providedids);
2593 }
2594
2595 static void
2596 select_patches(Pool *pool, Queue *job)
2597 {
2598   Id p, pp;
2599   int pruneyou = 0;
2600   Map installedmap, noobsmap;
2601   Solvable *s;
2602
2603   map_init(&noobsmap, 0);
2604   map_init(&installedmap, pool->nsolvables);
2605   solver_calculate_noobsmap(pool, job, &noobsmap);
2606   if (pool->installed)
2607     FOR_REPO_SOLVABLES(pool->installed, p, s)
2608       MAPSET(&installedmap, p);
2609
2610   /* install all patches */
2611   for (p = 1; p < pool->nsolvables; p++)
2612     {
2613       const char *type;
2614       int r;
2615       Id p2;
2616
2617       s = pool->solvables + p;
2618       if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
2619         continue;
2620       FOR_PROVIDES(p2, pp, s->name)
2621         {
2622           Solvable *s2 = pool->solvables + p2;
2623           if (s2->name != s->name)
2624             continue;
2625           r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2626           if (r < 0 || (r == 0 && p > p2))
2627             break;
2628         }
2629       if (p2)
2630         continue;
2631       type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2632       if (type && !strcmp(type, "optional"))
2633         continue;
2634       r = solvable_trivial_installable_map(s, &installedmap, 0, &noobsmap);
2635       if (r == -1)
2636         continue;
2637       if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2638         {
2639           if (!pruneyou++)
2640             queue_empty(job);
2641         }
2642       else if (pruneyou)
2643         continue;
2644       queue_push2(job, SOLVER_SOLVABLE, p);
2645     }
2646   map_free(&installedmap);
2647   map_free(&noobsmap);
2648 }
2649
2650 #define MODE_LIST        0
2651 #define MODE_INSTALL     1
2652 #define MODE_ERASE       2
2653 #define MODE_UPDATE      3
2654 #define MODE_DISTUPGRADE 4
2655 #define MODE_VERIFY      5
2656 #define MODE_PATCH       6
2657 #define MODE_INFO        7
2658 #define MODE_REPOLIST    8
2659 #define MODE_SEARCH      9
2660
2661 void
2662 usage(int r)
2663 {
2664   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2665   fprintf(stderr, "\n");
2666   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2667   fprintf(stderr, "                  versions from the repositories\n");
2668   fprintf(stderr, "    erase:        erase installed packages\n");
2669   fprintf(stderr, "    info:         display package information\n");
2670   fprintf(stderr, "    install:      install packages\n");
2671   fprintf(stderr, "    list:         list packages\n");
2672   fprintf(stderr, "    repos:        list enabled repositories\n");
2673   fprintf(stderr, "    search:       search name/summary/description\n");
2674   fprintf(stderr, "    update:       update installed packages\n");
2675   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2676   fprintf(stderr, "\n");
2677   exit(r);
2678 }
2679
2680 int
2681 main(int argc, char **argv)
2682 {
2683   Pool *pool;
2684   Repo *commandlinerepo = 0;
2685   Id *commandlinepkgs = 0;
2686   Id p, pp;
2687   struct repoinfo *repoinfos;
2688   int nrepoinfos = 0;
2689   int mainmode = 0, mode = 0;
2690   int i, newpkgs;
2691   Queue job, checkq;
2692   Solver *solv = 0;
2693   Transaction *trans;
2694   char inbuf[128], *ip;
2695   int allpkgs = 0;
2696   FILE **newpkgsfps;
2697   Queue addedfileprovides;
2698   Queue addedfileprovides_inst;
2699   Id repofilter = 0;
2700   int cleandeps = 0;
2701
2702   argc--;
2703   argv++;
2704   if (!argv[0])
2705     usage(1);
2706   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2707     {
2708       mainmode = MODE_INSTALL;
2709       mode = SOLVER_INSTALL;
2710     }
2711   else if (!strcmp(argv[0], "patch"))
2712     {
2713       mainmode = MODE_PATCH;
2714       mode = SOLVER_INSTALL;
2715     }
2716   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2717     {
2718       mainmode = MODE_ERASE;
2719       mode = SOLVER_ERASE;
2720     }
2721   else if (!strcmp(argv[0], "list"))
2722     {
2723       mainmode = MODE_LIST;
2724       mode = 0;
2725     }
2726   else if (!strcmp(argv[0], "info"))
2727     {
2728       mainmode = MODE_INFO;
2729       mode = 0;
2730     }
2731   else if (!strcmp(argv[0], "search"))
2732     {
2733       mainmode = MODE_SEARCH;
2734       mode = 0;
2735     }
2736   else if (!strcmp(argv[0], "verify"))
2737     {
2738       mainmode = MODE_VERIFY;
2739       mode = SOLVER_VERIFY;
2740     }
2741   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2742     {
2743       mainmode = MODE_UPDATE;
2744       mode = SOLVER_UPDATE;
2745     }
2746   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2747     {
2748       mainmode = MODE_DISTUPGRADE;
2749       mode = SOLVER_UPDATE;
2750     }
2751   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2752     {
2753       mainmode = MODE_REPOLIST;
2754       mode = 0;
2755     }
2756   else
2757     usage(1);
2758
2759   if (argc > 1 && !strcmp(argv[1], "--clean"))
2760     {
2761       cleandeps = 1;
2762       argc--;
2763       argv++;
2764     }
2765
2766   pool = pool_create();
2767
2768 #if 0
2769   {
2770     const char *langs[] = {"de_DE", "de", "en"};
2771     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
2772   }
2773 #endif
2774
2775   pool_setloadcallback(pool, load_stub, 0);
2776   pool->nscallback = nscallback;
2777   // pool_setdebuglevel(pool, 2);
2778   setarch(pool);
2779   repoinfos = read_repoinfos(pool, &nrepoinfos);
2780
2781   if (mainmode == MODE_REPOLIST)
2782     {
2783       int j = 1;
2784       for (i = 0; i < nrepoinfos; i++)
2785         {
2786           struct repoinfo *cinfo = repoinfos + i;
2787           if (!cinfo->enabled)
2788             continue;
2789           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
2790         }
2791       exit(0);
2792     }
2793
2794   read_repos(pool, repoinfos, nrepoinfos);
2795
2796   if (argc > 2 && !strcmp(argv[1], "-r"))
2797     {
2798       const char *rname = argv[2], *rp;
2799       for (rp = rname; *rp; rp++)
2800         if (*rp <= '0' || *rp >= '9')
2801           break;
2802       if (!*rp)
2803         {
2804           /* repo specified by number */
2805           int rnum = atoi(rname);
2806           for (i = 0; i < nrepoinfos; i++)
2807             {
2808               struct repoinfo *cinfo = repoinfos + i;
2809               if (!cinfo->enabled)
2810                 continue;
2811               if (--rnum == 0)
2812                 repofilter = cinfo->repo->repoid;
2813             }
2814         }
2815       else
2816         {
2817           /* repo specified by alias */
2818           Repo *repo;
2819           FOR_REPOS(i, repo)
2820             {
2821               if (!strcasecmp(rname, repo->name))
2822                 repofilter = repo->repoid;
2823             }
2824         }
2825       if (!repofilter)
2826         {
2827           fprintf(stderr, "%s: no such repo\n", rname);
2828           exit(1);
2829         }
2830       argc -= 2;
2831       argv += 2;
2832     }
2833   if (mainmode == MODE_SEARCH)
2834     {
2835       Dataiterator di;
2836       Map m;
2837       if (argc != 2)
2838         usage(1);
2839       map_init(&m, pool->nsolvables);
2840       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2841       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2842       dataiterator_set_search(&di, 0, 0);
2843       while (dataiterator_step(&di))
2844         MAPSET(&m, di.solvid);
2845       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2846       dataiterator_set_search(&di, 0, 0);
2847       while (dataiterator_step(&di))
2848         MAPSET(&m, di.solvid);
2849       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2850       dataiterator_set_search(&di, 0, 0);
2851       while (dataiterator_step(&di))
2852         MAPSET(&m, di.solvid);
2853       dataiterator_free(&di);
2854
2855       for (p = 1; p < pool->nsolvables; p++)
2856         {
2857           Solvable *s = pool_id2solvable(pool, p);
2858           if (!MAPTST(&m, p))
2859             continue;
2860           printf("  - %s: %s\n", pool_solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2861         }
2862       map_free(&m);
2863       exit(0);
2864     }
2865
2866
2867   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2868     {
2869       for (i = 1; i < argc; i++)
2870         {
2871           int l;
2872           l = strlen(argv[i]);
2873 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2874           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2875             continue;
2876 #endif
2877 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2878           if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
2879             continue;
2880 #endif
2881           if (access(argv[i], R_OK))
2882             {
2883               perror(argv[i]);
2884               exit(1);
2885             }
2886           if (!commandlinepkgs)
2887             commandlinepkgs = solv_calloc(argc, sizeof(Id));
2888           if (!commandlinerepo)
2889             commandlinerepo = repo_create(pool, "@commandline");
2890           p = 0;
2891 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
2892           p = repo_add_rpm(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2893 #endif
2894 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
2895           p = repo_add_deb(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2896 #endif
2897           if (!p)
2898             {
2899               fprintf(stderr, "could not add '%s'\n", argv[i]);
2900               exit(1);
2901             }
2902           commandlinepkgs[i] = p;
2903         }
2904       if (commandlinerepo)
2905         repo_internalize(commandlinerepo);
2906     }
2907
2908   // FOR_REPOS(i, repo)
2909   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2910   queue_init(&addedfileprovides);
2911   queue_init(&addedfileprovides_inst);
2912   pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
2913   if (addedfileprovides.count || addedfileprovides_inst.count)
2914     rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
2915   queue_free(&addedfileprovides);
2916   queue_free(&addedfileprovides_inst);
2917   pool_createwhatprovides(pool);
2918
2919   queue_init(&job);
2920   for (i = 1; i < argc; i++)
2921     {
2922       Queue job2;
2923       int j;
2924
2925       if (commandlinepkgs && commandlinepkgs[i])
2926         {
2927           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2928           continue;
2929         }
2930       queue_init(&job2);
2931       if (!mkselect(pool, mode, argv[i], &job2))
2932         {
2933           fprintf(stderr, "nothing matches '%s'\n", argv[i]);
2934           exit(1);
2935         }
2936       if (repofilter && !limitrepo(pool, repofilter, &job2))
2937         {
2938           fprintf(stderr, "nothing in repo matches '%s'\n", argv[i]);
2939           exit(1);
2940         }
2941       for (j = 0; j < job2.count; j++)
2942         queue_push(&job, job2.elements[j]);
2943       queue_free(&job2);
2944     }
2945
2946   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2947     {
2948       printf("no package matched\n");
2949       exit(1);
2950     }
2951
2952   if (!job.count)
2953     allpkgs = 1;
2954
2955   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2956     {
2957       /* list mode, no solver needed */
2958       for (i = 0; i < job.count; i += 2)
2959         {
2960           Id how = job.elements[i] & SOLVER_SELECTMASK;
2961           FOR_JOB_SELECT(p, pp, how, job.elements[i + 1])
2962             {
2963               Solvable *s = pool_id2solvable(pool, p);
2964               if (mainmode == MODE_INFO)
2965                 {
2966                   const char *str;
2967                   printf("Name:        %s\n", pool_solvable2str(pool, s));
2968                   printf("Repo:        %s\n", s->repo->name);
2969                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2970                   str = solvable_lookup_str(s, SOLVABLE_URL);
2971                   if (str)
2972                     printf("Url:         %s\n", str);
2973                   str = solvable_lookup_str(s, SOLVABLE_LICENSE);
2974                   if (str)
2975                     printf("License:     %s\n", str);
2976                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2977                   printf("\n");
2978                 }
2979               else
2980                 {
2981 #if 1
2982                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
2983 #else
2984                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
2985 #endif
2986                   printf("  - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
2987                   if (sum)
2988                     printf("    %s\n", sum);
2989                 }
2990             }
2991         }
2992       queue_free(&job);
2993       pool_free(pool);
2994       free_repoinfos(repoinfos, nrepoinfos);
2995       solv_free(commandlinepkgs);
2996 #ifdef FEDORA
2997       yum_substitute(pool, 0);
2998 #endif
2999       exit(0);
3000     }
3001
3002   if (mainmode == MODE_PATCH)
3003     select_patches(pool, &job);
3004
3005   // add mode
3006   for (i = 0; i < job.count; i += 2)
3007     {
3008       if (mode == SOLVER_UPDATE)
3009         {
3010           /* make update of not installed packages an install */
3011           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
3012             if (pool->installed && pool->solvables[p].repo == pool->installed)
3013               break;
3014           if (!p)
3015             {
3016               job.elements[i] |= SOLVER_INSTALL;
3017               if (cleandeps)
3018                 job.elements[i] |= SOLVER_CLEANDEPS;
3019               continue;
3020             }
3021         }
3022       job.elements[i] |= mode;
3023       if (cleandeps)
3024         job.elements[i] |= SOLVER_CLEANDEPS;
3025     }
3026
3027   if (mainmode == MODE_DISTUPGRADE && allpkgs)
3028     {
3029       if (repofilter)
3030         queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter);
3031       else
3032         queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_ALL, 0);
3033     }
3034   if (mainmode == MODE_UPDATE && allpkgs)
3035     {
3036       if (repofilter)
3037         queue_push2(&job, SOLVER_UPDATE|SOLVER_SOLVABLE_REPO, repofilter);
3038       else
3039         queue_push2(&job, SOLVER_UPDATE|SOLVER_SOLVABLE_ALL, 0);
3040     }
3041   if (mainmode == MODE_VERIFY && allpkgs)
3042     {
3043       if (repofilter)
3044         queue_push2(&job, SOLVER_VERIFY|SOLVER_SOLVABLE_REPO, repofilter);
3045       else
3046         queue_push2(&job, SOLVER_VERIFY|SOLVER_SOLVABLE_ALL, 0);
3047     }
3048
3049   // multiversion test
3050   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
3051   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
3052   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
3053
3054 #ifdef SOFTLOCKS_PATH
3055   addsoftlocks(pool, &job);
3056 #endif
3057
3058 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3059 rerunsolver:
3060 #endif
3061   for (;;)
3062     {
3063       Id problem, solution;
3064       int pcnt, scnt;
3065
3066       solv = solver_create(pool);
3067       solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
3068       if (mainmode == MODE_ERASE)
3069         solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);  /* don't nag */
3070
3071       if (!solver_solve(solv, &job))
3072         break;
3073       pcnt = solver_problem_count(solv);
3074       printf("Found %d problems:\n", pcnt);
3075       for (problem = 1; problem <= pcnt; problem++)
3076         {
3077           int take = 0;
3078           printf("Problem %d:\n", problem);
3079           solver_printprobleminfo(solv, problem);
3080           printf("\n");
3081           scnt = solver_solution_count(solv, problem);
3082           for (solution = 1; solution <= scnt; solution++)
3083             {
3084               printf("Solution %d:\n", solution);
3085               solver_printsolution(solv, problem, solution);
3086               printf("\n");
3087             }
3088           for (;;)
3089             {
3090               printf("Please choose a solution: ");
3091               fflush(stdout);
3092               *inbuf = 0;
3093               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
3094                 {
3095                   printf("Abort.\n");
3096                   exit(1);
3097                 }
3098               while (*ip == ' ' || *ip == '\t')
3099                 ip++;
3100               if (*ip >= '0' && *ip <= '9')
3101                 {
3102                   take = atoi(ip);
3103                   if (take >= 1 && take <= scnt)
3104                     break;
3105                 }
3106               if (*ip == 's')
3107                 {
3108                   take = 0;
3109                   break;
3110                 }
3111               if (*ip == 'q')
3112                 {
3113                   printf("Abort.\n");
3114                   exit(1);
3115                 }
3116             }
3117           if (!take)
3118             continue;
3119           solver_take_solution(solv, problem, take, &job);
3120         }
3121       solver_free(solv);
3122       solv = 0;
3123     }
3124
3125   trans = solver_create_transaction(solv);
3126   if (!trans->steps.count)
3127     {
3128       printf("Nothing to do.\n");
3129       transaction_free(trans);
3130       solver_free(solv);
3131       queue_free(&job);
3132       pool_free(pool);
3133       free_repoinfos(repoinfos, nrepoinfos);
3134       solv_free(commandlinepkgs);
3135 #ifdef FEDORA
3136       yum_substitute(pool, 0);
3137 #endif
3138       exit(1);
3139     }
3140   printf("\n");
3141   printf("Transaction summary:\n\n");
3142   transaction_print(trans);
3143
3144 #if defined(SUSE)
3145   if (1)
3146     {
3147       DUChanges duc[4];
3148       int i;
3149
3150       duc[0].path = "/";
3151       duc[1].path = "/usr/share/man";
3152       duc[2].path = "/sbin";
3153       duc[3].path = "/etc";
3154       transaction_calc_duchanges(trans, duc, 4);
3155       for (i = 0; i < 4; i++)
3156         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
3157     }
3158 #endif
3159   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
3160   printf("\n");
3161
3162   if (!yesno("OK to continue (y/n)? "))
3163     {
3164       printf("Abort.\n");
3165       transaction_free(trans);
3166       solver_free(solv);
3167       queue_free(&job);
3168       pool_free(pool);
3169       free_repoinfos(repoinfos, nrepoinfos);
3170       solv_free(commandlinepkgs);
3171 #ifdef FEDORA
3172       yum_substitute(pool, 0);
3173 #endif
3174       exit(1);
3175     }
3176
3177   queue_init(&checkq);
3178   newpkgs = transaction_installedresult(trans, &checkq);
3179   newpkgsfps = 0;
3180
3181   if (newpkgs)
3182     {
3183       int downloadsize = 0;
3184       for (i = 0; i < newpkgs; i++)
3185         {
3186           Solvable *s;
3187
3188           p = checkq.elements[i];
3189           s = pool_id2solvable(pool, p);
3190           downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
3191         }
3192       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
3193       newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
3194       for (i = 0; i < newpkgs; i++)
3195         {
3196           unsigned int medianr;
3197           char *loc;
3198           Solvable *s;
3199           struct repoinfo *cinfo;
3200           const unsigned char *chksum;
3201           Id chksumtype;
3202           Dataiterator di;
3203
3204           p = checkq.elements[i];
3205           s = pool_id2solvable(pool, p);
3206           if (s->repo == commandlinerepo)
3207             {
3208               loc = solvable_get_location(s, &medianr);
3209               if (!(newpkgsfps[i] = fopen(loc, "r")))
3210                 {
3211                   perror(loc);
3212                   exit(1);
3213                 }
3214               putchar('.');
3215               continue;
3216             }
3217           cinfo = s->repo->appdata;
3218           if (!cinfo)
3219             {
3220               printf("%s: no repository information\n", s->repo->name);
3221               exit(1);
3222             }
3223           loc = solvable_get_location(s, &medianr);
3224           if (!loc)
3225              continue;
3226
3227           if (pool->installed && pool->installed->nsolvables)
3228             {
3229               /* try a delta first */
3230               char *matchname = strdup(pool_id2str(pool, s->name));
3231               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
3232               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
3233               while (dataiterator_step(&di))
3234                 {
3235                   Id baseevr, op;
3236
3237                   dataiterator_setpos_parent(&di);
3238                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
3239                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
3240                     continue;
3241                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
3242                   FOR_PROVIDES(op, pp, s->name)
3243                     {
3244                       Solvable *os = pool->solvables + op;
3245                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
3246                         break;
3247                     }
3248                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
3249                     {
3250                       /* base is installed, run sequence check */
3251                       const char *seqname;
3252                       const char *seqevr;
3253                       const char *seqnum;
3254                       const char *seq;
3255                       const char *dloc;
3256                       const char *archstr;
3257                       FILE *fp;
3258                       char cmd[128];
3259                       int newfd;
3260
3261                       archstr = pool_id2str(pool, s->arch);
3262                       if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
3263                         continue;
3264
3265                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
3266                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
3267                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
3268                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
3269                       seq = pool_tmpappend(pool, seq, "-", seqnum);
3270                       if (strchr(seq, '\'') != 0)
3271                         continue;
3272 #ifdef FEDORA
3273                       sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
3274 #else
3275                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
3276 #endif
3277                       if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
3278                         continue;       /* didn't match */
3279                       /* looks good, download delta */
3280                       chksumtype = 0;
3281                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
3282                       if (!chksumtype)
3283                         continue;       /* no way! */
3284                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
3285                       dloc = pool_tmpappend(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
3286                       dloc = pool_tmpappend(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
3287                       dloc = pool_tmpappend(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
3288                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
3289                         continue;
3290                       /* got it, now reconstruct */
3291                       newfd = opentmpfile();
3292 #ifdef FEDORA
3293                       sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
3294 #else
3295                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
3296 #endif
3297                       fcntl(fileno(fp), F_SETFD, 0);
3298                       if (system(cmd))
3299                         {
3300                           close(newfd);
3301                           fclose(fp);
3302                           continue;
3303                         }
3304                       lseek(newfd, 0, SEEK_SET);
3305                       chksumtype = 0;
3306                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3307                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
3308                         {
3309                           close(newfd);
3310                           fclose(fp);
3311                           continue;
3312                         }
3313                       newpkgsfps[i] = fdopen(newfd, "r");
3314                       fclose(fp);
3315                       break;
3316                     }
3317                 }
3318               dataiterator_free(&di);
3319               solv_free(matchname);
3320             }
3321
3322           if (newpkgsfps[i])
3323             {
3324               putchar('d');
3325               fflush(stdout);
3326               continue;         /* delta worked! */
3327             }
3328           if (cinfo->type == TYPE_SUSETAGS)
3329             {
3330               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
3331               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
3332             }
3333           chksumtype = 0;
3334           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3335           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
3336             {
3337               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
3338               exit(1);
3339             }
3340           putchar('.');
3341           fflush(stdout);
3342         }
3343       putchar('\n');
3344     }
3345
3346 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3347   if (newpkgs)
3348     {
3349       Queue conflicts;
3350       struct fcstate fcstate;
3351
3352       printf("Searching for file conflicts\n");
3353       queue_init(&conflicts);
3354       fcstate.rpmdbstate = 0;
3355       fcstate.newpkgscnt = newpkgs;
3356       fcstate.checkq = &checkq;
3357       fcstate.newpkgsfps = newpkgsfps;
3358       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
3359       if (conflicts.count)
3360         {
3361           printf("\n");
3362           for (i = 0; i < conflicts.count; i += 5)
3363             printf("file %s of package %s conflicts with package %s\n", pool_id2str(pool, conflicts.elements[i]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_solvid2str(pool, conflicts.elements[i + 3]));
3364           printf("\n");
3365           if (yesno("Re-run solver (y/n/q)? "))
3366             {
3367               for (i = 0; i < newpkgs; i++)
3368                 if (newpkgsfps[i])
3369                   fclose(newpkgsfps[i]);
3370               newpkgsfps = solv_free(newpkgsfps);
3371               solver_free(solv);
3372               pool_add_fileconflicts_deps(pool, &conflicts);
3373               pool_createwhatprovides(pool);    /* Hmm... */
3374               goto rerunsolver;
3375             }
3376         }
3377       queue_free(&conflicts);
3378     }
3379 #endif
3380
3381   printf("Committing transaction:\n\n");
3382   transaction_order(trans, 0);
3383   for (i = 0; i < trans->steps.count; i++)
3384     {
3385 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3386       const char *evr, *evrp, *nvra;
3387 #endif
3388       Solvable *s;
3389       int j;
3390       FILE *fp;
3391
3392       p = trans->steps.elements[i];
3393       s = pool_id2solvable(pool, p);
3394       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
3395       switch(type)
3396         {
3397         case SOLVER_TRANSACTION_ERASE:
3398           printf("erase %s\n", pool_solvid2str(pool, p));
3399 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3400           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
3401             continue;
3402           /* strip epoch from evr */
3403           evr = evrp = pool_id2str(pool, s->evr);
3404           while (*evrp >= '0' && *evrp <= '9')
3405             evrp++;
3406           if (evrp > evr && evrp[0] == ':' && evrp[1])
3407             evr = evrp + 1;
3408           nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
3409           nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
3410           runrpm("-e", nvra, -1);       /* too bad that --querybynumber doesn't work */
3411 #endif
3412 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
3413           rundpkg("--remove", pool_id2str(pool, s->name), 0);
3414 #endif
3415           break;
3416         case SOLVER_TRANSACTION_INSTALL:
3417         case SOLVER_TRANSACTION_MULTIINSTALL:
3418           printf("install %s\n", pool_solvid2str(pool, p));
3419           for (j = 0; j < newpkgs; j++)
3420             if (checkq.elements[j] == p)
3421               break;
3422           fp = j < newpkgs ? newpkgsfps[j] : 0;
3423           if (!fp)
3424             continue;
3425           rewind(fp);
3426           lseek(fileno(fp), 0, SEEK_SET);
3427 #if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
3428           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
3429 #endif
3430 #if defined(ENABLE_DEBIAN) && defined(DEBIAN)
3431           rundpkg("--install", "/dev/fd/3", fileno(fp));
3432 #endif
3433           fclose(fp);
3434           newpkgsfps[j] = 0;
3435           break;
3436         default:
3437           break;
3438         }
3439     }
3440
3441   for (i = 0; i < newpkgs; i++)
3442     if (newpkgsfps[i])
3443       fclose(newpkgsfps[i]);
3444   solv_free(newpkgsfps);
3445   queue_free(&checkq);
3446   transaction_free(trans);
3447   solver_free(solv);
3448   queue_free(&job);
3449   pool_free(pool);
3450   free_repoinfos(repoinfos, nrepoinfos);
3451   solv_free(commandlinepkgs);
3452 #ifdef FEDORA
3453   yum_substitute(pool, 0);
3454 #endif
3455   exit(0);
3456 }