summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt11
-rw-r--r--VERSION.cmake2
-rw-r--r--bindings/solv.i1
-rwxr-xr-xdoc/filters/xcode.pl2
-rw-r--r--doc/libsolv-bindings.313
-rw-r--r--doc/libsolv-bindings.txt5
-rw-r--r--examples/CMakeLists.txt11
-rw-r--r--examples/solv.c3415
-rw-r--r--examples/solv/CMakeLists.txt29
-rw-r--r--examples/solv/checksig.c126
-rw-r--r--examples/solv/checksig.h3
-rw-r--r--examples/solv/deltarpm.c133
-rw-r--r--examples/solv/deltarpm.h1
-rw-r--r--examples/solv/fastestmirror.c149
-rw-r--r--examples/solv/fastestmirror.h2
-rw-r--r--examples/solv/fileconflicts.c76
-rw-r--r--examples/solv/fileconflicts.h2
-rw-r--r--examples/solv/fileprovides.c94
-rw-r--r--examples/solv/fileprovides.h1
-rw-r--r--examples/solv/mirror.c110
-rw-r--r--examples/solv/mirror.h2
-rw-r--r--examples/solv/patchjobs.c63
-rw-r--r--examples/solv/patchjobs.h2
-rw-r--r--examples/solv/repoinfo.c275
-rw-r--r--examples/solv/repoinfo.h53
-rw-r--r--examples/solv/repoinfo_cache.c290
-rw-r--r--examples/solv/repoinfo_cache.h12
-rw-r--r--examples/solv/repoinfo_config_debian.c127
-rw-r--r--examples/solv/repoinfo_config_debian.h1
-rw-r--r--examples/solv/repoinfo_config_urpmi.c106
-rw-r--r--examples/solv/repoinfo_config_urpmi.h2
-rw-r--r--examples/solv/repoinfo_config_yum.c233
-rw-r--r--examples/solv/repoinfo_config_yum.h2
-rw-r--r--examples/solv/repoinfo_download.c221
-rw-r--r--examples/solv/repoinfo_download.h7
-rw-r--r--examples/solv/repoinfo_system_debian.c108
-rw-r--r--examples/solv/repoinfo_system_debian.h2
-rw-r--r--examples/solv/repoinfo_system_rpm.c151
-rw-r--r--examples/solv/repoinfo_system_rpm.h2
-rw-r--r--examples/solv/repoinfo_type_debian.c202
-rw-r--r--examples/solv/repoinfo_type_debian.h2
-rw-r--r--examples/solv/repoinfo_type_mdk.c213
-rw-r--r--examples/solv/repoinfo_type_mdk.h3
-rw-r--r--examples/solv/repoinfo_type_rpmmd.c207
-rw-r--r--examples/solv/repoinfo_type_rpmmd.h2
-rw-r--r--examples/solv/repoinfo_type_susetags.c274
-rw-r--r--examples/solv/repoinfo_type_susetags.h2
-rw-r--r--examples/solv/solv.c910
-rw-r--r--ext/repo_autopattern.c9
-rw-r--r--ext/repo_mdk.c2
-rw-r--r--ext/testcase.c25
-rw-r--r--package/libsolv.changes13
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/hash.h4
-rw-r--r--src/libsolv.ver2
-rw-r--r--src/order.c1345
-rw-r--r--src/solver.c80
-rw-r--r--src/solver.h7
-rw-r--r--src/transaction.c1244
-rw-r--r--src/transaction.h15
-rw-r--r--tools/installcheck.c66
61 files changed, 5739 insertions, 4735 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2ba307c..c089e21 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -111,13 +111,22 @@ SET (have_system ${have_system}x)
ENDIF (ARCHLINUX)
IF (MANDRIVA)
-MESSAGE (STATUS "Building for Mandriva/Mageia")
+MESSAGE (STATUS "Building for Mandriva")
ADD_DEFINITIONS (-DMANDRIVA)
SET (ENABLE_MDKREPO ON)
SET (ENABLE_RPMDB ON)
SET (have_system ${have_system}x)
ENDIF (MANDRIVA)
+IF (MAGEIA)
+MESSAGE (STATUS "Building for Mageia")
+ADD_DEFINITIONS (-DMAGEIA)
+SET (ENABLE_MDKREPO ON)
+SET (ENABLE_RPMDB ON)
+SET (ENABLE_LZMA_COMPRESSION ON)
+SET (have_system ${have_system}x)
+ENDIF (MAGEIA)
+
IF (HAIKU)
MESSAGE(STATUS "Building for Haiku")
FIND_LIBRARY(HAIKU_BE_LIBRARY NAMES be)
diff --git a/VERSION.cmake b/VERSION.cmake
index 086ee95..f67959d 100644
--- a/VERSION.cmake
+++ b/VERSION.cmake
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "0")
SET(LIBSOLV_MAJOR "0")
SET(LIBSOLV_MINOR "6")
-SET(LIBSOLV_PATCH "12")
+SET(LIBSOLV_PATCH "13")
diff --git a/bindings/solv.i b/bindings/solv.i
index 0c37f6c..823deda 100644
--- a/bindings/solv.i
+++ b/bindings/solv.i
@@ -1171,6 +1171,7 @@ typedef struct {
static const Id SOLVER_VERIFY = SOLVER_VERIFY;
static const Id SOLVER_DROP_ORPHANED = SOLVER_DROP_ORPHANED;
static const Id SOLVER_USERINSTALLED = SOLVER_USERINSTALLED;
+ static const Id SOLVER_ALLOWUNINSTALL = SOLVER_ALLOWUNINSTALL;
static const Id SOLVER_JOBMASK = SOLVER_JOBMASK;
static const Id SOLVER_WEAK = SOLVER_WEAK;
static const Id SOLVER_ESSENTIAL = SOLVER_ESSENTIAL;
diff --git a/doc/filters/xcode.pl b/doc/filters/xcode.pl
index 089f511..407641f 100755
--- a/doc/filters/xcode.pl
+++ b/doc/filters/xcode.pl
@@ -36,7 +36,7 @@ while(<STDIN>) {
$_ = " $_";
$_ = "$_ ";
if (s/^ TCL +/ /) {
- s/(\$[a-zA-Z_][a-zA-Z0-9_]*)/<-S><I>$1<-I><S>/g;
+ s/(\$[a-zA-Z_][a-zA-Z0-9_:]*)/<-S><I>$1<-I><S>/g;
} else {
s/(?<=[^a-zA-Z_\&:\.\'\";])(?!solv\W|Solv\W|Pool\W)([\$\@a-zA-Z_][a-zA-Z0-9_]*)(?=[^a-zA-Z0-9_\(;\[])(?!::)(?! [^=])/<-S><I>$1<-I><S>/g;
}
diff --git a/doc/libsolv-bindings.3 b/doc/libsolv-bindings.3
index 9fe26c1..ad8a7e6 100644
--- a/doc/libsolv-bindings.3
+++ b/doc/libsolv-bindings.3
@@ -2,12 +2,12 @@
.\" Title: Libsolv-Bindings
.\" Author: [see the "Author" section]
.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 08/28/2015
+.\" Date: 09/21/2015
.\" Manual: LIBSOLV
.\" Source: libsolv
.\" Language: English
.\"
-.TH "LIBSOLV\-BINDINGS" "3" "08/28/2015" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-BINDINGS" "3" "09/21/2015" "libsolv" "LIBSOLV"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -399,8 +399,8 @@ Swig implements all constants as numeric variables, constants belonging to a lib
.RS 4
.\}
.nf
-\fI$pool\fR \fBset_flag\fR \fI$solv\fR\fB::Pool_POOL_FLAG_OBSOLETEUSESCOLORS 1\fR
-\fBputs [\fR\fI$solvable\fR \fBlookup_str\fR \fI$solv\fR\fB::SOLVABLE_SUMMARY]\fR
+\fI$pool\fR \fBset_flag\fR \fI$solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS\fR \fB1\fR
+\fBputs [\fR\fI$solvable\fR \fBlookup_str\fR \fI$solv::SOLVABLE_SUMMARY\fR\fB]\fR
.fi
.if n \{\
.RE
@@ -3251,6 +3251,11 @@ Fix dependency problems of matching installed packages\&. The default is to igno
The matching installed packages are considered to be installed by a user, thus not installed to fulfill some dependency\&. This is needed input for the calculation of unneeded packages for jobs that have the SOLVER_CLEANDEPS flag set\&.
.RE
.PP
+\fBSOLVER_ALLOWUNINSTALL\fR
+.RS 4
+Allow the solver to deinstall the matching installed packages if they get into the way of resolving a dependency\&. This is like the SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages\&.
+.RE
+.PP
\fBSOLVER_JOBMASK\fR
.RS 4
A mask containing all the above action bits\&.
diff --git a/doc/libsolv-bindings.txt b/doc/libsolv-bindings.txt
index 175d922..13d73bd 100644
--- a/doc/libsolv-bindings.txt
+++ b/doc/libsolv-bindings.txt
@@ -1873,6 +1873,11 @@ thus not installed to fulfill some dependency. This is needed input for
the calculation of unneeded packages for jobs that have the
SOLVER_CLEANDEPS flag set.
+*SOLVER_ALLOWUNINSTALL*::
+Allow the solver to deinstall the matching installed packages if they get
+into the way of resolving a dependency. This is like the
+SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages.
+
*SOLVER_JOBMASK*::
A mask containing all the above action bits.
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index d29ea68..703796c 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,10 +1,5 @@
-IF (SUSE OR FEDORA OR DEBIAN)
+IF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
-ADD_EXECUTABLE (solv solv.c)
-TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
+ADD_SUBDIRECTORY (solv)
-INSTALL(TARGETS
- solv
- DESTINATION ${BIN_INSTALL_DIR})
-
-ENDIF (SUSE OR FEDORA OR DEBIAN)
+ENDIF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
diff --git a/examples/solv.c b/examples/solv.c
deleted file mode 100644
index fc420b3..0000000
--- a/examples/solv.c
+++ /dev/null
@@ -1,3415 +0,0 @@
-/*
- * Copyright (c) 2009-2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* solv, a little software installer demoing the sat solver library */
-
-/* things it does:
- * - understands globs for package names / dependencies
- * - understands .arch suffix
- * - installation of commandline packages
- * - repository data caching
- * - on demand loading of secondary repository data
- * - gpg and checksum verification
- * - file conflicts
- * - deltarpm support
- * - fastestmirror implementation
- *
- * things available in the library but missing from solv:
- * - vendor policy loading
- * - soft locks file handling
- * - multi version handling
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <fnmatch.h>
-#include <unistd.h>
-#include <zlib.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <sys/utsname.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/dir.h>
-#include <sys/stat.h>
-
-#include <sys/socket.h>
-#include <netdb.h>
-#include <poll.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "poolarch.h"
-#include "repo.h"
-#include "evr.h"
-#include "policy.h"
-#include "util.h"
-#include "solver.h"
-#include "solverdebug.h"
-#include "chksum.h"
-#include "repo_solv.h"
-#include "selection.h"
-
-#include "repo_write.h"
-#ifdef ENABLE_RPMDB
-#include "repo_rpmdb.h"
-#include "pool_fileconflicts.h"
-#endif
-#ifdef ENABLE_PUBKEY
-#include "repo_pubkey.h"
-#endif
-#ifdef ENABLE_DEBIAN
-#include "repo_deb.h"
-#endif
-#ifdef ENABLE_RPMMD
-#include "repo_rpmmd.h"
-#include "repo_repomdxml.h"
-#include "repo_updateinfoxml.h"
-#include "repo_deltainfoxml.h"
-#endif
-#ifdef ENABLE_APPDATA
-#include "repo_appdata.h"
-#endif
-#ifdef ENABLE_SUSEREPO
-#include "repo_products.h"
-#include "repo_susetags.h"
-#include "repo_content.h"
-#endif
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "solv_xfopen.h"
-
-#ifdef FEDORA
-# define REPOINFO_PATH "/etc/yum.repos.d"
-#endif
-#ifdef SUSE
-# define REPOINFO_PATH "/etc/zypp/repos.d"
-# define PRODUCTS_PATH "/etc/products.d"
-# define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
-#endif
-#ifdef ENABLE_APPDATA
-# define APPDATA_PATH "/usr/share/appdata"
-#endif
-
-#define SOLVCACHE_PATH "/var/cache/solv"
-
-#define METADATA_EXPIRE (60 * 15)
-
-struct repoinfo {
- Repo *repo;
-
- char *alias;
- char *name;
- int enabled;
- int autorefresh;
- char *baseurl;
- char *metalink;
- char *mirrorlist;
- char *path;
- int type;
- int pkgs_gpgcheck;
- int repo_gpgcheck;
- int priority;
- int keeppackages;
- int metadata_expire;
- char **components;
- int ncomponents;
- unsigned char cookie[32];
- int extcookieset;
- unsigned char extcookie[32];
- int incomplete;
-};
-
-#ifdef FEDORA
-char *
-yum_substitute(Pool *pool, char *line)
-{
- char *p, *p2;
- static char *releaseevr;
- static char *basearch;
-
- if (!line)
- {
- solv_free(releaseevr);
- releaseevr = 0;
- solv_free(basearch);
- basearch = 0;
- return 0;
- }
- p = line;
- while ((p2 = strchr(p, '$')) != 0)
- {
- if (!strncmp(p2, "$releasever", 11))
- {
- if (!releaseevr)
- {
- void *rpmstate;
- Queue q;
-
- queue_init(&q);
- rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
- rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
- if (q.count)
- {
- void *handle;
- char *p;
- handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
- releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
- if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
- *p = 0;
- }
- rpm_state_free(rpmstate);
- queue_free(&q);
- if (!releaseevr)
- {
- fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
- exit(1);
- }
- }
- *p2 = 0;
- p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
- p2 = p + (p2 - line);
- line = p;
- p = p2 + strlen(releaseevr);
- continue;
- }
- if (!strncmp(p2, "$basearch", 9))
- {
- if (!basearch)
- {
- struct utsname un;
- if (uname(&un))
- {
- perror("uname");
- exit(1);
- }
- basearch = strdup(un.machine);
- if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
- basearch[1] = '3';
- }
- *p2 = 0;
- p = pool_tmpjoin(pool, line, basearch, p2 + 9);
- p2 = p + (p2 - line);
- line = p;
- p = p2 + strlen(basearch);
- continue;
- }
- p = p2 + 1;
- }
- return line;
-}
-#endif
-
-#define TYPE_UNKNOWN 0
-#define TYPE_SUSETAGS 1
-#define TYPE_RPMMD 2
-#define TYPE_PLAINDIR 3
-#define TYPE_DEBIAN 4
-
-static int
-read_repoinfos_sort(const void *ap, const void *bp)
-{
- const struct repoinfo *a = ap;
- const struct repoinfo *b = bp;
- return strcmp(a->alias, b->alias);
-}
-
-#if defined(SUSE) || defined(FEDORA)
-
-struct repoinfo *
-read_repoinfos(Pool *pool, int *nrepoinfosp)
-{
- const char *reposdir = REPOINFO_PATH;
- char buf[4096];
- char buf2[4096], *kp, *vp, *kpe;
- DIR *dir;
- FILE *fp;
- struct dirent *ent;
- int l, rdlen;
- struct repoinfo *repoinfos = 0, *cinfo;
- int nrepoinfos = 0;
-
- rdlen = strlen(reposdir);
- dir = opendir(reposdir);
- if (!dir)
- {
- *nrepoinfosp = 0;
- return 0;
- }
- while ((ent = readdir(dir)) != 0)
- {
- if (ent->d_name[0] == '.')
- continue;
- l = strlen(ent->d_name);
- if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
- continue;
- snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
- if ((fp = fopen(buf, "r")) == 0)
- {
- perror(buf);
- continue;
- }
- cinfo = 0;
- while(fgets(buf2, sizeof(buf2), fp))
- {
- l = strlen(buf2);
- if (l == 0)
- continue;
- while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
- buf2[--l] = 0;
- kp = buf2;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp || *kp == '#')
- continue;
-#ifdef FEDORA
- if (strchr(kp, '$'))
- kp = yum_substitute(pool, kp);
-#endif
- if (*kp == '[')
- {
- vp = strrchr(kp, ']');
- if (!vp)
- continue;
- *vp = 0;
- repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
- cinfo = repoinfos + nrepoinfos++;
- memset(cinfo, 0, sizeof(*cinfo));
- cinfo->alias = strdup(kp + 1);
- cinfo->type = TYPE_RPMMD;
- cinfo->autorefresh = 1;
- cinfo->priority = 99;
-#ifndef FEDORA
- cinfo->repo_gpgcheck = 1;
-#endif
- cinfo->metadata_expire = METADATA_EXPIRE;
- continue;
- }
- if (!cinfo)
- continue;
- vp = strchr(kp, '=');
- if (!vp)
- continue;
- for (kpe = vp - 1; kpe >= kp; kpe--)
- if (*kpe != ' ' && *kpe != '\t')
- break;
- if (kpe == kp)
- continue;
- vp++;
- while (*vp == ' ' || *vp == '\t')
- vp++;
- kpe[1] = 0;
- if (!strcmp(kp, "name"))
- cinfo->name = strdup(vp);
- else if (!strcmp(kp, "enabled"))
- cinfo->enabled = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "autorefresh"))
- cinfo->autorefresh = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "gpgcheck"))
- cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "repo_gpgcheck"))
- cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
- else if (!strcmp(kp, "baseurl"))
- cinfo->baseurl = strdup(vp);
- else if (!strcmp(kp, "mirrorlist"))
- {
- if (strstr(vp, "metalink"))
- cinfo->metalink = strdup(vp);
- else
- cinfo->mirrorlist = strdup(vp);
- }
- else if (!strcmp(kp, "path"))
- {
- if (vp && strcmp(vp, "/") != 0)
- cinfo->path = strdup(vp);
- }
- else if (!strcmp(kp, "type"))
- {
- if (!strcmp(vp, "yast2"))
- cinfo->type = TYPE_SUSETAGS;
- else if (!strcmp(vp, "rpm-md"))
- cinfo->type = TYPE_RPMMD;
- else if (!strcmp(vp, "plaindir"))
- cinfo->type = TYPE_PLAINDIR;
- else
- cinfo->type = TYPE_UNKNOWN;
- }
- else if (!strcmp(kp, "priority"))
- cinfo->priority = atoi(vp);
- else if (!strcmp(kp, "keeppackages"))
- cinfo->keeppackages = *vp == '0' ? 0 : 1;
- }
- fclose(fp);
- cinfo = 0;
- }
- closedir(dir);
- qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
- *nrepoinfosp = nrepoinfos;
- return repoinfos;
-}
-
-#endif
-
-#ifdef DEBIAN
-
-struct repoinfo *
-read_repoinfos(Pool *pool, int *nrepoinfosp)
-{
- FILE *fp;
- char buf[4096];
- char buf2[4096];
- int l;
- char *kp, *url, *distro;
- struct repoinfo *repoinfos = 0, *cinfo;
- int nrepoinfos = 0;
- DIR *dir = 0;
- struct dirent *ent;
-
- fp = fopen("/etc/apt/sources.list", "r");
- while (1)
- {
- if (!fp)
- {
- if (!dir)
- {
- dir = opendir("/etc/apt/sources.list.d");
- if (!dir)
- break;
- }
- if ((ent = readdir(dir)) == 0)
- {
- closedir(dir);
- break;
- }
- if (ent->d_name[0] == '.')
- continue;
- l = strlen(ent->d_name);
- if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
- continue;
- snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
- if (!(fp = fopen(buf, "r")))
- continue;
- }
- while(fgets(buf2, sizeof(buf2), fp))
- {
- l = strlen(buf2);
- if (l == 0)
- continue;
- while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
- buf2[--l] = 0;
- kp = buf2;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp || *kp == '#')
- continue;
- if (strncmp(kp, "deb", 3) != 0)
- continue;
- kp += 3;
- if (*kp != ' ' && *kp != '\t')
- continue;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- continue;
- url = kp;
- while (*kp && *kp != ' ' && *kp != '\t')
- kp++;
- if (*kp)
- *kp++ = 0;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- continue;
- distro = kp;
- while (*kp && *kp != ' ' && *kp != '\t')
- kp++;
- if (*kp)
- *kp++ = 0;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- continue;
- repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
- cinfo = repoinfos + nrepoinfos++;
- memset(cinfo, 0, sizeof(*cinfo));
- cinfo->baseurl = strdup(url);
- cinfo->alias = solv_dupjoin(url, "/", distro);
- cinfo->name = strdup(distro);
- cinfo->type = TYPE_DEBIAN;
- cinfo->enabled = 1;
- cinfo->autorefresh = 1;
- cinfo->repo_gpgcheck = 1;
- cinfo->metadata_expire = METADATA_EXPIRE;
- while (*kp)
- {
- char *compo;
- while (*kp == ' ' || *kp == '\t')
- kp++;
- if (!*kp)
- break;
- compo = kp;
- while (*kp && *kp != ' ' && *kp != '\t')
- kp++;
- if (*kp)
- *kp++ = 0;
- cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
- cinfo->components[cinfo->ncomponents++] = strdup(compo);
- }
- }
- fclose(fp);
- fp = 0;
- }
- qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
- *nrepoinfosp = nrepoinfos;
- return repoinfos;
-}
-
-#endif
-
-
-void
-free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
-{
- int i, j;
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- solv_free(cinfo->name);
- solv_free(cinfo->alias);
- solv_free(cinfo->path);
- solv_free(cinfo->metalink);
- solv_free(cinfo->mirrorlist);
- solv_free(cinfo->baseurl);
- for (j = 0; j < cinfo->ncomponents; j++)
- solv_free(cinfo->components[j]);
- solv_free(cinfo->components);
- }
- solv_free(repoinfos);
-}
-
-static inline int
-opentmpfile()
-{
- char tmpl[100];
- int fd;
-
- strcpy(tmpl, "/var/tmp/solvXXXXXX");
- fd = mkstemp(tmpl);
- if (fd < 0)
- {
- perror("mkstemp");
- exit(1);
- }
- unlink(tmpl);
- return fd;
-}
-
-static int
-verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
-{
- char buf[1024];
- const unsigned char *sum;
- Chksum *h;
- int l;
-
- h = solv_chksum_create(chksumtype);
- if (!h)
- {
- printf("%s: unknown checksum type\n", file);
- return 0;
- }
- while ((l = read(fd, buf, sizeof(buf))) > 0)
- solv_chksum_add(h, buf, l);
- lseek(fd, 0, SEEK_SET);
- l = 0;
- sum = solv_chksum_get(h, &l);
- if (memcmp(sum, chksum, l))
- {
- printf("%s: checksum mismatch\n", file);
- solv_chksum_free(h, 0);
- return 0;
- }
- solv_chksum_free(h, 0);
- return 1;
-}
-
-void
-findfastest(char **urls, int nurls)
-{
- int i, j, port;
- int *socks, qc;
- struct pollfd *fds;
- char *p, *p2, *q;
- char portstr[16];
- struct addrinfo hints, *result;;
-
- fds = solv_calloc(nurls, sizeof(*fds));
- socks = solv_calloc(nurls, sizeof(*socks));
- for (i = 0; i < nurls; i++)
- {
- socks[i] = -1;
- p = strchr(urls[i], '/');
- if (!p)
- continue;
- if (p[1] != '/')
- continue;
- p += 2;
- q = strchr(p, '/');
- qc = 0;
- if (q)
- {
- qc = *q;
- *q = 0;
- }
- if ((p2 = strchr(p, '@')) != 0)
- p = p2 + 1;
- port = 80;
- if (!strncmp("https:", urls[i], 6))
- port = 443;
- else if (!strncmp("ftp:", urls[i], 4))
- port = 21;
- if ((p2 = strrchr(p, ':')) != 0)
- {
- port = atoi(p2 + 1);
- if (q)
- *q = qc;
- q = p2;
- qc = *q;
- *q = 0;
- }
- sprintf(portstr, "%d", port);
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_NUMERICSERV;
- result = 0;
- if (!getaddrinfo(p, portstr, &hints, &result))
- {
- socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
- if (socks[i] >= 0)
- {
- fcntl(socks[i], F_SETFL, O_NONBLOCK);
- if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
- {
- if (errno != EINPROGRESS)
- {
- close(socks[i]);
- socks[i] = -1;
- }
- }
- }
- freeaddrinfo(result);
- }
- if (q)
- *q = qc;
- }
- for (;;)
- {
- for (i = j = 0; i < nurls; i++)
- {
- if (socks[i] < 0)
- continue;
- fds[j].fd = socks[i];
- fds[j].events = POLLOUT;
- j++;
- }
- if (j < 2)
- {
- i = j - 1;
- break;
- }
- if (poll(fds, j, 10000) <= 0)
- {
- i = -1; /* something is wrong */
- break;
- }
- for (i = 0; i < j; i++)
- if ((fds[i].revents & POLLOUT) != 0)
- {
- int soe = 0;
- socklen_t soel = sizeof(int);
- if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
- {
- /* connect failed, kill socket */
- for (j = 0; j < nurls; j++)
- if (socks[j] == fds[i].fd)
- {
- close(socks[j]);
- socks[j] = -1;
- }
- i = j + 1;
- break;
- }
- break; /* horray! */
- }
- if (i == j + 1)
- continue;
- if (i == j)
- i = -1; /* something is wrong, no bit was set */
- break;
- }
- /* now i contains the fastest fd index */
- if (i >= 0)
- {
- for (j = 0; j < nurls; j++)
- if (socks[j] == fds[i].fd)
- break;
- if (j != 0)
- {
- char *url0 = urls[0];
- urls[0] = urls[j];
- urls[j] = url0;
- }
- }
- for (i = j = 0; i < nurls; i++)
- if (socks[i] >= 0)
- close(socks[i]);
- free(socks);
- free(fds);
-}
-
-char *
-findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
-{
- char buf[4096], *bp, *ep;
- char **urls = 0;
- int nurls = 0;
- int i;
-
- if (chksumtypep)
- *chksumtypep = 0;
- while((bp = fgets(buf, sizeof(buf), fp)) != 0)
- {
- while (*bp == ' ' || *bp == '\t')
- bp++;
- if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
- {
- bp += 20;
- if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
- *chksumtypep = REPOKEY_TYPE_SHA256;
- continue;
- }
- if (strncmp(bp, "<url", 4))
- continue;
- bp = strchr(bp, '>');
- if (!bp)
- continue;
- bp++;
- ep = strstr(bp, "repodata/repomd.xml</url>");
- if (!ep)
- continue;
- *ep = 0;
- if (strncmp(bp, "http", 4))
- continue;
- urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
- urls[nurls++] = strdup(bp);
- }
- if (nurls)
- {
- if (nurls > 1)
- findfastest(urls, nurls > 5 ? 5 : nurls);
- bp = urls[0];
- urls[0] = 0;
- for (i = 0; i < nurls; i++)
- solv_free(urls[i]);
- solv_free(urls);
- ep = strchr(bp, '/');
- if ((ep = strchr(ep + 2, '/')) != 0)
- {
- *ep = 0;
- printf("[using mirror %s]\n", bp);
- *ep = '/';
- }
- return bp;
- }
- return 0;
-}
-
-char *
-findmirrorlisturl(FILE *fp)
-{
- char buf[4096], *bp, *ep;
- int i, l;
- char **urls = 0;
- int nurls = 0;
-
- while((bp = fgets(buf, sizeof(buf), fp)) != 0)
- {
- while (*bp == ' ' || *bp == '\t')
- bp++;
- if (!*bp || *bp == '#')
- continue;
- l = strlen(bp);
- while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
- bp[--l] = 0;
- urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
- urls[nurls++] = strdup(bp);
- }
- if (nurls)
- {
- if (nurls > 1)
- findfastest(urls, nurls > 5 ? 5 : nurls);
- bp = urls[0];
- urls[0] = 0;
- for (i = 0; i < nurls; i++)
- solv_free(urls[i]);
- solv_free(urls);
- ep = strchr(bp, '/');
- if ((ep = strchr(ep + 2, '/')) != 0)
- {
- *ep = 0;
- printf("[using mirror %s]\n", bp);
- *ep = '/';
- }
- return bp;
- }
- return 0;
-}
-
-static inline int
-iscompressed(const char *name)
-{
- return solv_xfopen_iscompressed(name) != 0;
-}
-
-FILE *
-curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
-{
- FILE *fp;
- pid_t pid;
- int fd, l;
- int status;
- char url[4096];
- const char *baseurl = cinfo->baseurl;
-
- if (!baseurl)
- {
- if (!cinfo->metalink && !cinfo->mirrorlist)
- return 0;
- if (file != cinfo->metalink && file != cinfo->mirrorlist)
- {
- unsigned char mlchksum[32];
- Id mlchksumtype;
- fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
- mlchksumtype = 0;
- if (!fp)
- return 0;
- if (cinfo->metalink)
- cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
- else
- cinfo->baseurl = findmirrorlisturl(fp);
- fclose(fp);
- if (!cinfo->baseurl)
- return 0;
-#ifdef FEDORA
- if (strchr(cinfo->baseurl, '$'))
- {
- char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
- free(cinfo->baseurl);
- cinfo->baseurl = strdup(b);
- }
-#endif
- if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
- {
- chksumtype = mlchksumtype;
- chksum = mlchksum;
- }
- return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
- }
- snprintf(url, sizeof(url), "%s", file);
- }
- else
- {
- l = strlen(baseurl);
- if (l && baseurl[l - 1] == '/')
- snprintf(url, sizeof(url), "%s%s", baseurl, file);
- else
- snprintf(url, sizeof(url), "%s/%s", baseurl, file);
- }
- fd = opentmpfile();
- // printf("url: %s\n", url);
- if ((pid = fork()) == (pid_t)-1)
- {
- perror("fork");
- exit(1);
- }
- if (pid == 0)
- {
- if (fd != 1)
- {
- dup2(fd, 1);
- close(fd);
- }
- execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
- perror("curl");
- _exit(0);
- }
- status = 0;
- while (waitpid(pid, &status, 0) != pid)
- ;
- if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
- {
- /* empty file */
- close(fd);
- return 0;
- }
- lseek(fd, 0, SEEK_SET);
- if (status)
- {
- printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
- if (markincomplete)
- cinfo->incomplete = 1;
- close(fd);
- return 0;
- }
- if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
- {
- if (markincomplete)
- cinfo->incomplete = 1;
- close(fd);
- return 0;
- }
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (uncompress)
- {
- if (solv_xfopen_iscompressed(file) < 0)
- {
- printf("%s: unsupported compression\n", file);
- if (markincomplete)
- cinfo->incomplete = 1;
- close(fd);
- return 0;
- }
- fp = solv_xfopen_fd(file, fd, "r");
- }
- else
- fp = fdopen(fd, "r");
- if (!fp)
- close(fd);
- return fp;
-}
-
-#ifndef DEBIAN
-
-static void
-cleanupgpg(char *gpgdir)
-{
- char cmd[256];
- snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
- unlink(cmd);
- snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
- unlink(cmd);
- rmdir(gpgdir);
-}
-
-int
-checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
-{
- char *gpgdir;
- char *keysfile;
- const char *pubkey;
- char cmd[256];
- FILE *kfp;
- Solvable *s;
- Id p;
- off_t posfp, possigfp;
- int r, nkeys;
-
- gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
- if (!gpgdir)
- return 0;
- keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
- if (!(kfp = fopen(keysfile, "w")) )
- {
- cleanupgpg(gpgdir);
- return 0;
- }
- nkeys = 0;
- for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
- {
- if (!s->repo)
- continue;
- pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
- if (!pubkey || !*pubkey)
- continue;
- if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
- break;
- if (fputc('\n', kfp) == EOF) /* Just in case... */
- break;
- nkeys++;
- }
- if (fclose(kfp) || !nkeys)
- {
- cleanupgpg(gpgdir);
- return 0;
- }
- snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
- if (system(cmd))
- {
- fprintf(stderr, "key import error\n");
- cleanupgpg(gpgdir);
- return 0;
- }
- unlink(keysfile);
- posfp = lseek(fileno(fp), 0, SEEK_CUR);
- lseek(fileno(fp), 0, SEEK_SET);
- possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
- lseek(fileno(sigfp), 0, SEEK_SET);
- snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
- fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
- fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
- r = system(cmd);
- lseek(fileno(sigfp), possigfp, SEEK_SET);
- lseek(fileno(fp), posfp, SEEK_SET);
- fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
- fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
- cleanupgpg(gpgdir);
- return r == 0 ? 1 : 0;
-}
-
-#else
-
-static int
-checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
-{
- char cmd[256];
- int r;
-
- 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));
- fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
- fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
- r = system(cmd);
- fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
- fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
- return r == 0 ? 1 : 0;
-}
-
-#endif
-
-static Pool *
-read_sigs()
-{
- Pool *sigpool = pool_create();
-#if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
- Repo *repo = repo_create(sigpool, "pubkeys");
- repo_add_rpmdb_pubkeys(repo, 0);
-#endif
- return sigpool;
-}
-
-static int
-downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
-{
- FILE *sigfp;
- sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0);
- if (!sigfp)
- {
- printf(" unsigned, skipped\n");
- return 0;
- }
- if (!*sigpool)
- *sigpool = read_sigs();
- if (!checksig(*sigpool, fp, sigfp))
- {
- printf(" checksig failed, skipped\n");
- fclose(sigfp);
- return 0;
- }
- fclose(sigfp);
- return 1;
-}
-
-#define CHKSUM_IDENT "1.1"
-
-void
-calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
-{
- char buf[4096];
- Chksum *h = solv_chksum_create(chktype);
- int l;
-
- solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
- while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
- solv_chksum_add(h, buf, l);
- rewind(fp);
- solv_chksum_free(h, out);
-}
-
-void
-calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
-{
- Chksum *h = solv_chksum_create(chktype);
- solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
- if (cookie)
- solv_chksum_add(h, cookie, 32);
- solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
- solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
- solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
- solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
- solv_chksum_free(h, out);
-}
-
-void
-setarch(Pool *pool)
-{
- struct utsname un;
- if (uname(&un))
- {
- perror("uname");
- exit(1);
- }
- pool_setarch(pool, un.machine);
-}
-
-char *userhome;
-
-char *
-calccachepath(Repo *repo, const char *repoext, int forcesystemloc)
-{
- char *q, *p;
- int l;
- if (!forcesystemloc && userhome && getuid())
- p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
- else
- p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
- l = strlen(p);
- p = pool_tmpappend(repo->pool, p, repo->name, 0);
- if (repoext)
- {
- p = pool_tmpappend(repo->pool, p, "_", repoext);
- p = pool_tmpappend(repo->pool, p, ".solvx", 0);
- }
- else
- p = pool_tmpappend(repo->pool, p, ".solv", 0);
- q = p + l;
- if (*q == '.')
- *q = '_';
- for (; *q; q++)
- if (*q == '/')
- *q = '_';
- return p;
-}
-
-int
-usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
-{
- FILE *fp;
- unsigned char mycookie[32];
- unsigned char myextcookie[32];
- struct repoinfo *cinfo;
- int flags;
- int forcesystemloc;
-
- forcesystemloc = mark & 2 ? 0 : 1;
- if (mark < 2 && userhome && getuid())
- {
- /* first try home location */
- int res = usecachedrepo(repo, repoext, cookie, mark | 2);
- if (res)
- return res;
- }
- mark &= 1;
- cinfo = repo->appdata;
- if (!(fp = fopen(calccachepath(repo, repoext, forcesystemloc), "r")))
- return 0;
- if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
- {
- fclose(fp);
- return 0;
- }
- if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
- {
- fclose(fp);
- return 0;
- }
- if (cinfo && !repoext)
- {
- if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
- {
- fclose(fp);
- return 0;
- }
- }
- rewind(fp);
-
- flags = 0;
- if (repoext)
- {
- flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
- if (strcmp(repoext, "DL") != 0)
- flags |= REPO_LOCALPOOL; /* no local pool for DL so that we can compare IDs */
- }
-
- if (repo_add_solv(repo, fp, flags))
- {
- fclose(fp);
- return 0;
- }
- if (cinfo && !repoext)
- {
- memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
- memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
- cinfo->extcookieset = 1;
- }
- if (mark)
- futimens(fileno(fp), 0); /* try to set modification time */
- fclose(fp);
- return 1;
-}
-
-void
-writecachedrepo(Repo *repo, Repodata *repodata, const char *repoext, unsigned char *cookie)
-{
- FILE *fp;
- int i, fd;
- char *tmpl, *cachedir;
- struct repoinfo *cinfo;
- int onepiece;
-
- cinfo = repo->appdata;
- if (cinfo && cinfo->incomplete)
- return;
- cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
- if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
- printf("[created %s]\n", cachedir);
- /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
- tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
- fd = mkstemp(tmpl);
- if (fd < 0)
- {
- free(tmpl);
- return;
- }
- fchmod(fd, 0444);
- if (!(fp = fdopen(fd, "w")))
- {
- close(fd);
- unlink(tmpl);
- free(tmpl);
- return;
- }
-
- onepiece = 1;
- for (i = repo->start; i < repo->end; i++)
- if (repo->pool->solvables[i].repo != repo)
- break;
- if (i < repo->end)
- onepiece = 0;
-
- if (!repodata)
- repo_write(repo, fp);
- else if (repoext)
- repodata_write(repodata, fp);
- else
- {
- int oldnrepodata = repo->nrepodata;
- repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata; /* XXX: do this right */
- repo_write(repo, fp);
- repo->nrepodata = oldnrepodata;
- onepiece = 0; /* don't bother for the added file provides */
- }
-
- if (!repoext && cinfo)
- {
- if (!cinfo->extcookieset)
- {
- /* create the ext cookie and append it */
- /* we just need some unique ID */
- struct stat stb;
- if (!fstat(fileno(fp), &stb))
- memset(&stb, 0, sizeof(stb));
- calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie, cinfo->extcookie);
- cinfo->extcookieset = 1;
- }
- if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
- {
- fclose(fp);
- unlink(tmpl);
- free(tmpl);
- return;
- }
- }
- /* append our cookie describing the metadata state */
- if (fwrite(cookie, 32, 1, fp) != 1)
- {
- fclose(fp);
- unlink(tmpl);
- free(tmpl);
- return;
- }
- if (fclose(fp))
- {
- unlink(tmpl);
- free(tmpl);
- return;
- }
- if (onepiece)
- {
- /* switch to just saved repo to activate paging and save memory */
- FILE *fp = fopen(tmpl, "r");
- if (fp)
- {
- if (!repoext)
- {
- /* main repo */
- repo_empty(repo, 1);
- if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
- {
- /* oops, no way to recover from here */
- fprintf(stderr, "internal error\n");
- exit(1);
- }
- }
- else
- {
- int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
- /* make sure repodata contains complete repo */
- /* (this is how repodata_write saves it) */
- repodata_extend_block(repodata, repo->start, repo->end - repo->start);
- repodata->state = REPODATA_LOADING;
- if (strcmp(repoext, "DL") != 0)
- flags |= REPO_LOCALPOOL;
- repo_add_solv(repo, fp, flags);
- repodata->state = REPODATA_AVAILABLE; /* in case the load failed */
- }
- fclose(fp);
- }
- }
- if (!rename(tmpl, calccachepath(repo, repoext, 0)))
- unlink(tmpl);
- free(tmpl);
-}
-
-
-#ifdef ENABLE_RPMMD
-/* repomd helpers */
-
-static inline const char *
-repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
-{
- Pool *pool = repo->pool;
- Dataiterator di;
- const char *filename;
-
- filename = 0;
- *chksump = 0;
- *chksumtypep = 0;
- dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
- dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
- if (dataiterator_step(&di))
- {
- dataiterator_setpos_parent(&di);
- filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
- *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
- }
- dataiterator_free(&di);
- if (filename && !*chksumtypep)
- {
- printf("no %s file checksum!\n", what);
- filename = 0;
- }
- return filename;
-}
-
-int
-repomd_add_ext(Repo *repo, Repodata *data, const char *what)
-{
- Id chksumtype, handle;
- const unsigned char *chksum;
- const char *filename;
-
- filename = repomd_find(repo, what, &chksum, &chksumtype);
- if (!filename)
- return 0;
- if (!strcmp(what, "prestodelta"))
- what = "deltainfo";
- handle = repodata_new_handle(data);
- repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
- repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
- repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
- if (!strcmp(what, "deltainfo"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
- }
- if (!strcmp(what, "filelists"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
- }
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
- return 1;
-}
-
-int
-repomd_load_ext(Repo *repo, Repodata *data)
-{
- const char *filename, *repomdtype;
- char ext[3];
- FILE *fp;
- struct repoinfo *cinfo;
- const unsigned char *filechksum;
- Id filechksumtype;
- int r = 0;
-
- cinfo = repo->appdata;
- repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
- if (!repomdtype)
- return 0;
- if (!strcmp(repomdtype, "filelists"))
- strcpy(ext, "FL");
- else if (!strcmp(repomdtype, "deltainfo"))
- strcpy(ext, "DL");
- else
- return 0;
- printf("[%s:%s", repo->name, ext);
- if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
- {
- printf(" cached]\n"); fflush(stdout);
- return 1;
- }
- printf(" fetching]\n"); fflush(stdout);
- filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
- filechksumtype = 0;
- filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
- if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
- return 0;
- if (!strcmp(ext, "FL"))
- r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
- else if (!strcmp(ext, "DL"))
- r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
- fclose(fp);
- if (r)
- {
- printf("%s\n", pool_errstr(repo->pool));
- return 0;
- }
- if (cinfo->extcookieset)
- writecachedrepo(repo, data, ext, cinfo->extcookie);
- return 1;
-}
-
-#endif
-
-
-#ifdef ENABLE_SUSEREPO
-/* susetags helpers */
-
-static inline const char *
-susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
-{
- Pool *pool = repo->pool;
- Dataiterator di;
- const char *filename;
-
- filename = 0;
- *chksump = 0;
- *chksumtypep = 0;
- dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
- dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
- if (dataiterator_step(&di))
- {
- dataiterator_setpos_parent(&di);
- *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
- filename = what;
- }
- dataiterator_free(&di);
- if (filename && !*chksumtypep)
- {
- printf("no %s file checksum!\n", what);
- filename = 0;
- }
- return filename;
-}
-
-static Id susetags_langtags[] = {
- SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
- SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
- SOLVABLE_EULA, REPOKEY_TYPE_STR,
- SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
- SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
- SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
- 0, 0
-};
-
-void
-susetags_add_ext(Repo *repo, Repodata *data)
-{
- Pool *pool = repo->pool;
- Dataiterator di;
- char ext[3];
- Id handle, filechksumtype;
- const unsigned char *filechksum;
- int i;
-
- dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
- dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
- while (dataiterator_step(&di))
- {
- if (strncmp(di.kv.str, "packages.", 9) != 0)
- continue;
- if (!strcmp(di.kv.str + 9, "gz"))
- continue;
- if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
- continue;
- ext[0] = di.kv.str[9];
- ext[1] = di.kv.str[10];
- ext[2] = 0;
- if (!strcmp(ext, "en"))
- continue;
- if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
- continue;
- handle = repodata_new_handle(data);
- repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
- if (filechksumtype)
- repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
- if (!strcmp(ext, "DU"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
- }
- else if (!strcmp(ext, "FL"))
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
- }
- else
- {
- for (i = 0; susetags_langtags[i]; i += 2)
- {
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
- repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
- }
- }
- repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
- }
- dataiterator_free(&di);
-}
-
-int
-susetags_load_ext(Repo *repo, Repodata *data)
-{
- const char *filename, *descrdir;
- Id defvendor;
- char ext[3];
- FILE *fp;
- struct repoinfo *cinfo;
- const unsigned char *filechksum;
- Id filechksumtype;
- int flags;
-
- cinfo = repo->appdata;
- filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
- if (!filename)
- return 0;
- /* susetags load */
- ext[0] = filename[9];
- ext[1] = filename[10];
- ext[2] = 0;
- printf("[%s:%s", repo->name, ext);
- if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
- {
- printf(" cached]\n"); fflush(stdout);
- return 1;
- }
- printf(" fetching]\n"); fflush(stdout);
- defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
- descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
- if (!descrdir)
- descrdir = "suse/setup/descr";
- filechksumtype = 0;
- filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
- if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
- return 0;
- flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
- if (strcmp(ext, "DL") != 0)
- flags |= REPO_LOCALPOOL;
- if (repo_add_susetags(repo, fp, defvendor, ext, flags))
- {
- fclose(fp);
- printf("%s\n", pool_errstr(repo->pool));
- return 0;
- }
- fclose(fp);
- if (cinfo->extcookieset)
- writecachedrepo(repo, data, ext, cinfo->extcookie);
- return 1;
-}
-#endif
-
-
-
-/* load callback */
-
-int
-load_stub(Pool *pool, Repodata *data, void *dp)
-{
- struct repoinfo *cinfo = data->repo->appdata;
- switch (cinfo->type)
- {
-#ifdef ENABLE_SUSEREPO
- case TYPE_SUSETAGS:
- return susetags_load_ext(data->repo, data);
-#endif
-#ifdef ENABLE_RPMMD
- case TYPE_RPMMD:
- return repomd_load_ext(data->repo, data);
-#endif
- default:
- return 0;
- }
-}
-
-static unsigned char installedcookie[32];
-
-#ifdef ENABLE_DEBIAN
-
-const char *
-debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
-{
- char buf[4096];
- Id chksumtype;
- unsigned char *chksum;
- Id curchksumtype;
- int l, compl;
- char *ch, *fn, *bp;
- char *filename;
- static char *basearch;
- char *binarydir;
- int lbinarydir;
-
- if (!basearch)
- {
- struct utsname un;
- if (uname(&un))
- {
- perror("uname");
- exit(1);
- }
- basearch = strdup(un.machine);
- if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
- basearch[1] = '3';
- }
- binarydir = solv_dupjoin("binary-", basearch, "/");
- lbinarydir = strlen(binarydir);
- compl = strlen(comp);
- rewind(fp);
- curchksumtype = 0;
- filename = 0;
- chksum = solv_malloc(32);
- chksumtype = 0;
- while(fgets(buf, sizeof(buf), fp))
- {
- l = strlen(buf);
- if (l == 0)
- continue;
- while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
- buf[--l] = 0;
- if (!strncasecmp(buf, "MD5Sum:", 7))
- {
- curchksumtype = REPOKEY_TYPE_MD5;
- continue;
- }
- if (!strncasecmp(buf, "SHA1:", 5))
- {
- curchksumtype = REPOKEY_TYPE_SHA1;
- continue;
- }
- if (!strncasecmp(buf, "SHA256:", 7))
- {
- curchksumtype = REPOKEY_TYPE_SHA256;
- continue;
- }
- if (!curchksumtype)
- continue;
- bp = buf;
- if (*bp++ != ' ')
- {
- curchksumtype = 0;
- continue;
- }
- ch = bp;
- while (*bp && *bp != ' ' && *bp != '\t')
- bp++;
- if (!*bp)
- continue;
- *bp++ = 0;
- while (*bp == ' ' || *bp == '\t')
- bp++;
- while (*bp && *bp != ' ' && *bp != '\t')
- bp++;
- if (!*bp)
- continue;
- while (*bp == ' ' || *bp == '\t')
- bp++;
- fn = bp;
- if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
- continue;
- bp += compl + 1;
- if (strncmp(bp, binarydir, lbinarydir))
- continue;
- bp += lbinarydir;
- if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
- {
- unsigned char curchksum[32];
- int curl;
- if (filename && !strcmp(bp, "Packages"))
- continue;
- curl = solv_chksum_len(curchksumtype);
- if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
- continue;
- if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
- continue;
- solv_free(filename);
- filename = strdup(fn);
- chksumtype = curchksumtype;
- memcpy(chksum, curchksum, curl);
- }
- }
- free(binarydir);
- if (filename)
- {
- fn = solv_dupjoin("/", filename, 0);
- solv_free(filename);
- filename = solv_dupjoin("dists/", cinfo->name, fn);
- solv_free(fn);
- }
- if (!chksumtype)
- chksum = solv_free(chksum);
- *chksump = chksum;
- *chksumtypep = chksumtype;
- return filename;
-}
-#endif
-
-void
-read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
-{
- Repo *repo;
- struct repoinfo *cinfo;
- int i;
- FILE *fp;
- const char *filename;
- const unsigned char *filechksum;
- Id filechksumtype;
-#ifdef ENABLE_SUSEREPO
- const char *descrdir;
- int defvendor;
-#endif
- struct stat stb;
- Pool *sigpool = 0;
-#if defined(ENABLE_SUSEREPO) || defined(ENABLE_RPMMD)
- Repodata *data;
-#endif
- int dorefresh;
-#if defined(ENABLE_DEBIAN)
- FILE *fpr;
- int j;
-#endif
-
- repo = repo_create(pool, "@System");
- memset(&stb, 0, sizeof(stb));
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- printf("rpm database:");
- if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
- memset(&stb, 0, sizeof(stb));
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- printf("dpgk database:");
- if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
- memset(&stb, 0, sizeof(stb));
-#endif
- calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, 0, installedcookie);
- if (usecachedrepo(repo, 0, installedcookie, 0))
- printf(" cached\n");
- else
- {
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- FILE *ofp = 0;
-#endif
- printf(" reading\n");
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-# if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
- if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
- exit(1);
- }
-# endif
-# if defined(ENABLE_APPDATA)
- if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
- exit(1);
- }
-# endif
- ofp = fopen(calccachepath(repo, 0, 0), "r");
- if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
- exit(1);
- }
- if (ofp)
- fclose(ofp);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
- {
- fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
- exit(1);
- }
-#endif
- repo_internalize(repo);
- writecachedrepo(repo, 0, 0, installedcookie);
- }
- pool_set_installed(pool, repo);
-
- for (i = 0; i < nrepoinfos; i++)
- {
- cinfo = repoinfos + i;
- if (!cinfo->enabled)
- continue;
-
- repo = repo_create(pool, cinfo->alias);
- cinfo->repo = repo;
- repo->appdata = cinfo;
- repo->priority = 99 - cinfo->priority;
-
- dorefresh = cinfo->autorefresh;
- if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0, 0), &stb) == 0)
- {
- if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
- dorefresh = 0;
- }
- if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
- {
- printf("repo '%s':", cinfo->alias);
- printf(" cached\n");
- continue;
- }
- switch (cinfo->type)
- {
-#ifdef ENABLE_RPMMD
- case TYPE_RPMMD:
- printf("rpmmd repo '%s':", cinfo->alias);
- fflush(stdout);
- if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
- {
- printf(" no repomd.xml file, skipped\n");
- repo_free(repo, 1);
- cinfo->repo = 0;
- break;
- }
- calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
- if (usecachedrepo(repo, 0, cinfo->cookie, 1))
- {
- printf(" cached\n");
- fclose(fp);
- break;
- }
- if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", &sigpool))
- {
- fclose(fp);
- break;
- }
- if (repo_add_repomdxml(repo, fp, 0))
- {
- printf("repomd.xml: %s\n", pool_errstr(pool));
- fclose(fp);
- break; /* hopeless */
- }
- fclose(fp);
- printf(" fetching\n");
- filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_rpmmd(repo, fp, 0, 0))
- {
- printf("primary: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- if (cinfo->incomplete)
- break; /* hopeless */
-
- filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_updateinfoxml(repo, fp, 0))
- {
- printf("updateinfo: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
-
-#ifdef ENABLE_APPDATA
- filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_appdata(repo, fp, 0))
- {
- printf("appdata: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
-#endif
- data = repo_add_repodata(repo, 0);
- if (!repomd_add_ext(repo, data, "deltainfo"))
- repomd_add_ext(repo, data, "prestodelta");
- repomd_add_ext(repo, data, "filelists");
- repodata_internalize(data);
- if (!cinfo->incomplete)
- writecachedrepo(repo, 0, 0, cinfo->cookie);
- repodata_create_stubs(repo_last_repodata(repo));
- break;
-#endif
-
-#ifdef ENABLE_SUSEREPO
- case TYPE_SUSETAGS:
- printf("susetags repo '%s':", cinfo->alias);
- fflush(stdout);
- descrdir = 0;
- defvendor = 0;
- if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
- {
- printf(" no content file, skipped\n");
- repo_free(repo, 1);
- cinfo->repo = 0;
- break;
- }
- calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
- if (usecachedrepo(repo, 0, cinfo->cookie, 1))
- {
- printf(" cached\n");
- fclose(fp);
- break;
- }
- if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", &sigpool))
- {
- fclose(fp);
- break;
- }
- if (repo_add_content(repo, fp, 0))
- {
- printf("content: %s\n", pool_errstr(pool));
- fclose(fp);
- break; /* hopeless */
- }
- fclose(fp);
- defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
- descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
- if (!descrdir)
- descrdir = "suse/setup/descr";
- filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
- if (!filename)
- filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
- if (!filename)
- {
- printf(" no packages file entry, skipped\n");
- break;
- }
- printf(" fetching\n");
- if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) == 0)
- break; /* hopeless */
- if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
- {
- printf("packages: %s\n", pool_errstr(pool));
- fclose(fp);
- cinfo->incomplete = 1;
- break; /* hopeless */
- }
- fclose(fp);
- /* add default language */
- filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
- if (!filename)
- filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
- if (filename)
- {
- if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
- {
- printf("packages.en: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- }
- filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
- if (filename)
- {
- if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- char pbuf[256];
- while (fgets(pbuf, sizeof(pbuf), fp))
- {
- int l = strlen(pbuf);
- FILE *fp2;
- if (l && pbuf[l - 1] == '\n')
- pbuf[--l] = 0;
- if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
- continue;
- filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
- if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
- {
- printf("%s: %s\n", pbuf, pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp2);
- }
- }
- fclose(fp);
- }
- }
-#ifdef ENABLE_APPDATA
- filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
- if (!filename)
- filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
- if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_appdata(repo, fp, 0))
- {
- printf("appdata: %s\n", pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
-#endif
- repo_internalize(repo);
- data = repo_add_repodata(repo, 0);
- susetags_add_ext(repo, data);
- repodata_internalize(data);
- if (!cinfo->incomplete)
- writecachedrepo(repo, 0, 0, cinfo->cookie);
- repodata_create_stubs(repo_last_repodata(repo));
- break;
-#endif
-
-#if defined(ENABLE_DEBIAN)
- case TYPE_DEBIAN:
- printf("debian repo '%s':", cinfo->alias);
- fflush(stdout);
- filename = solv_dupjoin("dists/", cinfo->name, "/Release");
- if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
- {
- printf(" no Release file, skipped\n");
- repo_free(repo, 1);
- cinfo->repo = 0;
- free((char *)filename);
- break;
- }
- solv_free((char *)filename);
- if (cinfo->repo_gpgcheck)
- {
- filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
- if (!downloadchecksig(cinfo, fpr, filename, &sigpool))
- {
- fclose(fpr);
- solv_free((char *)filename);
- break;
- }
- solv_free((char *)filename);
- }
- calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
- if (usecachedrepo(repo, 0, cinfo->cookie, 1))
- {
- printf(" cached\n");
- fclose(fpr);
- break;
- }
- printf(" fetching\n");
- for (j = 0; j < cinfo->ncomponents; j++)
- {
- if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
- {
- printf("[component %s not found]\n", cinfo->components[j]);
- continue;
- }
- if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
- {
- if (repo_add_debpackages(repo, fp, 0))
- {
- printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
- cinfo->incomplete = 1;
- }
- fclose(fp);
- }
- solv_free((char *)filechksum);
- solv_free((char *)filename);
- }
- fclose(fpr);
- if (!cinfo->incomplete)
- writecachedrepo(repo, 0, 0, cinfo->cookie);
- break;
-#endif
-
- default:
- printf("unsupported repo '%s': skipped\n", cinfo->alias);
- repo_free(repo, 1);
- cinfo->repo = 0;
- break;
- }
- }
- if (sigpool)
- pool_free(sigpool);
-}
-
-int
-yesno(const char *str)
-{
- char inbuf[128], *ip;
-
- for (;;)
- {
- printf("%s", str);
- fflush(stdout);
- *inbuf = 0;
- if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
- {
- printf("Abort.\n");
- exit(1);
- }
- while (*ip == ' ' || *ip == '\t')
- ip++;
- if (*ip == 'q')
- {
- printf("Abort.\n");
- exit(1);
- }
- if (*ip == 'y' || *ip == 'n')
- return *ip == 'y' ? 1 : 0;
- }
-}
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-
-struct fcstate {
- FILE **newpkgsfps;
- Queue *checkq;
- int newpkgscnt;
- void *rpmstate;
-};
-
-static void *
-fileconflict_cb(Pool *pool, Id p, void *cbdata)
-{
- struct fcstate *fcstate = cbdata;
- Solvable *s;
- Id rpmdbid;
- int i;
- FILE *fp;
-
- s = pool_id2solvable(pool, p);
- if (pool->installed && s->repo == pool->installed)
- {
- if (!s->repo->rpmdbid)
- return 0;
- rpmdbid = s->repo->rpmdbid[p - s->repo->start];
- if (!rpmdbid)
- return 0;
- return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
- }
- for (i = 0; i < fcstate->newpkgscnt; i++)
- if (fcstate->checkq->elements[i] == p)
- break;
- if (i == fcstate->newpkgscnt)
- return 0;
- fp = fcstate->newpkgsfps[i];
- if (!fp)
- return 0;
- rewind(fp);
- return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
-}
-
-
-void
-runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
-{
- pid_t pid;
- int status;
-
- if ((pid = fork()) == (pid_t)-1)
- {
- perror("fork");
- exit(1);
- }
- if (pid == 0)
- {
- if (!rootdir)
- rootdir = "/";
- if (dupfd3 != -1 && dupfd3 != 3)
- {
- dup2(dupfd3, 3);
- close(dupfd3);
- }
- if (dupfd3 != -1)
- fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
- if (strcmp(arg, "-e") == 0)
- execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
- else
- execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
- perror("rpm");
- _exit(0);
- }
- while (waitpid(pid, &status, 0) != pid)
- ;
- if (status)
- {
- printf("rpm failed\n");
- exit(1);
- }
-}
-
-#endif
-
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-
-void
-rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
-{
- pid_t pid;
- int status;
-
- if ((pid = fork()) == (pid_t)-1)
- {
- perror("fork");
- exit(1);
- }
- if (pid == 0)
- {
- if (!rootdir)
- rootdir = "/";
- if (dupfd3 != -1 && dupfd3 != 3)
- {
- dup2(dupfd3, 3);
- close(dupfd3);
- }
- if (dupfd3 != -1)
- fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
- if (strcmp(arg, "--install") == 0)
- execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
- else
- execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
- perror("dpkg");
- _exit(0);
- }
- while (waitpid(pid, &status, 0) != pid)
- ;
- if (status)
- {
- printf("dpkg failed\n");
- exit(1);
- }
-}
-
-#endif
-
-#ifdef SUSE
-static Id
-nscallback(Pool *pool, void *data, Id name, Id evr)
-{
-#if 0
- if (name == NAMESPACE_LANGUAGE)
- {
- if (!strcmp(pool_id2str(pool, evr), "ja"))
- return 1;
- if (!strcmp(pool_id2str(pool, evr), "de"))
- return 1;
- if (!strcmp(pool_id2str(pool, evr), "en"))
- return 1;
- if (!strcmp(pool_id2str(pool, evr), "en_US"))
- return 1;
- }
-#endif
- return 0;
-}
-#endif
-
-#ifdef SOFTLOCKS_PATH
-void
-addsoftlocks(Pool *pool, Queue *job)
-{
- FILE *fp;
- Id type, id, p, pp;
- char *bp, *ep, buf[4096];
-
- if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
- return;
- while((bp = fgets(buf, sizeof(buf), fp)) != 0)
- {
- while (*bp == ' ' || *bp == '\t')
- bp++;
- if (!*bp || *bp == '#')
- continue;
- for (ep = bp; *ep; ep++)
- if (*ep == ' ' || *ep == '\t' || *ep == '\n')
- break;
- *ep = 0;
- type = SOLVER_SOLVABLE_NAME;
- if (!strncmp(bp, "provides:", 9) && bp[9])
- {
- type = SOLVER_SOLVABLE_PROVIDES;
- bp += 9;
- }
- id = pool_str2id(pool, bp, 1);
- if (pool->installed)
- {
- FOR_JOB_SELECT(p, pp, type, id)
- if (pool->solvables[p].repo == pool->installed)
- break;
- if (p)
- continue; /* ignore, as it is already installed */
- }
- queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
- }
- fclose(fp);
-}
-#endif
-
-
-#if defined(ENABLE_RPMDB)
-
-static void
-rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
-{
- Repo *repo;
- Repodata *data;
- Map providedids;
- Queue fileprovidesq;
- int i, j, n;
- struct repoinfo *cinfo;
-
- map_init(&providedids, pool->ss.nstrings);
- queue_init(&fileprovidesq);
- for (i = 0; i < addedfileprovides->count; i++)
- MAPSET(&providedids, addedfileprovides->elements[i]);
- FOR_REPOS(i, repo)
- {
- /* make sure all repodatas but the first are extensions */
- if (repo->nrepodata < 2)
- continue;
- cinfo = repo->appdata;
- if (repo != pool->installed && !cinfo)
- continue;
- if (cinfo && cinfo->incomplete)
- continue;
- data = repo_id2repodata(repo, 1);
- if (data->loadcallback)
- continue;
- for (j = 2; j < repo->nrepodata; j++)
- {
- Repodata *edata = repo_id2repodata(repo, j);
- if (!edata->loadcallback)
- break;
- }
- if (j < repo->nrepodata)
- continue; /* found a non-externsion repodata, can't rewrite */
- if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
- {
- if (repo == pool->installed && addedfileprovides_inst)
- {
- for (j = 0; j < addedfileprovides->count; j++)
- MAPCLR(&providedids, addedfileprovides->elements[j]);
- for (j = 0; j < addedfileprovides_inst->count; j++)
- MAPSET(&providedids, addedfileprovides_inst->elements[j]);
- }
- n = 0;
- for (j = 0; j < fileprovidesq.count; j++)
- if (MAPTST(&providedids, fileprovidesq.elements[j]))
- n++;
- if (repo == pool->installed && addedfileprovides_inst)
- {
- for (j = 0; j < addedfileprovides_inst->count; j++)
- MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
- for (j = 0; j < addedfileprovides->count; j++)
- MAPSET(&providedids, addedfileprovides->elements[j]);
- if (n == addedfileprovides_inst->count)
- continue; /* nothing new added */
- }
- else if (n == addedfileprovides->count)
- continue; /* nothing new added */
- }
- repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
- repodata_internalize(data);
- writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
- }
- queue_free(&fileprovidesq);
- map_free(&providedids);
-}
-
-static void
-addfileprovides(Pool *pool)
-{
- Queue addedfileprovides;
- Queue addedfileprovides_inst;
-
- queue_init(&addedfileprovides);
- queue_init(&addedfileprovides_inst);
- pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
- if (addedfileprovides.count || addedfileprovides_inst.count)
- rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
- queue_free(&addedfileprovides);
- queue_free(&addedfileprovides_inst);
-}
-
-#endif
-
-#ifdef SUSE
-static void
-add_autopackages(Pool *pool)
-{
- int i;
- Repo *repo;
- FOR_REPOS(i, repo)
- repo_add_autopattern(repo, 0);
-}
-#endif
-
-#if defined(SUSE) || defined(FEDORA)
-static void
-add_patchjobs(Pool *pool, Queue *job)
-{
- Id p, pp;
- int pruneyou = 0;
- Map installedmap, multiversionmap;
- Solvable *s;
-
- map_init(&multiversionmap, 0);
- map_init(&installedmap, pool->nsolvables);
- solver_calculate_multiversionmap(pool, job, &multiversionmap);
- if (pool->installed)
- FOR_REPO_SOLVABLES(pool->installed, p, s)
- MAPSET(&installedmap, p);
-
- /* install all patches */
- for (p = 1; p < pool->nsolvables; p++)
- {
- const char *type;
- int r;
- Id p2;
-
- s = pool->solvables + p;
- if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
- continue;
- FOR_PROVIDES(p2, pp, s->name)
- {
- Solvable *s2 = pool->solvables + p2;
- if (s2->name != s->name)
- continue;
- r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
- if (r < 0 || (r == 0 && p > p2))
- break;
- }
- if (p2)
- continue;
- type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
- if (type && !strcmp(type, "optional"))
- continue;
- r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
- if (r == -1)
- continue;
- if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
- {
- if (!pruneyou++)
- queue_empty(job);
- }
- else if (pruneyou)
- continue;
- queue_push2(job, SOLVER_SOLVABLE, p);
- }
- map_free(&installedmap);
- map_free(&multiversionmap);
-}
-#endif
-
-#ifdef SUSE
-static void
-showdiskusagechanges(Transaction *trans)
-{
- DUChanges duc[4];
- int i;
-
- /* XXX: use mountpoints here */
- memset(duc, 0, sizeof(duc));
- duc[0].path = "/";
- duc[1].path = "/usr/share/man";
- duc[2].path = "/sbin";
- duc[3].path = "/etc";
- transaction_calc_duchanges(trans, duc, 4);
- for (i = 0; i < 4; i++)
- printf("duchanges %s: %d K %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
-}
-#endif
-
-#if defined(ENABLE_RPMDB)
-static FILE *
-trydeltadownload(Solvable *s, struct repoinfo *cinfo, const char *loc)
-{
- Pool *pool = s->repo->pool;
- Dataiterator di;
- Id pp;
- const unsigned char *chksum;
- Id chksumtype;
- FILE *retfp = 0;
- char *matchname = strdup(pool_id2str(pool, s->name));
-
- dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
- dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
- while (dataiterator_step(&di))
- {
- Id baseevr, op;
-
- dataiterator_setpos_parent(&di);
- if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
- pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
- continue;
- baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
- FOR_PROVIDES(op, pp, s->name)
- {
- Solvable *os = pool->solvables + op;
- if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
- break;
- }
- if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
- {
- /* base is installed, run sequence check */
- const char *seq;
- const char *dloc;
- const char *archstr;
- FILE *fp;
- char cmd[128];
- int newfd;
-
- archstr = pool_id2str(pool, s->arch);
- if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
- continue;
-
- seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
- seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
- if (strchr(seq, '\'') != 0)
- continue;
-#ifdef FEDORA
- sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
-#else
- sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
-#endif
- if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
- continue; /* didn't match */
- /* looks good, download delta */
- chksumtype = 0;
- chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
- if (!chksumtype)
- continue; /* no way! */
- dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
- if (!dloc)
- continue;
-#ifdef ENABLE_SUSEREPO
- if (cinfo->type == TYPE_SUSETAGS)
- {
- const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
- dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
- }
-#endif
- if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
- continue;
- /* got it, now reconstruct */
- newfd = opentmpfile();
-#ifdef FEDORA
- sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
-#else
- sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
-#endif
- fcntl(fileno(fp), F_SETFD, 0);
- if (system(cmd))
- {
- close(newfd);
- fclose(fp);
- continue;
- }
- lseek(newfd, 0, SEEK_SET);
- chksumtype = 0;
- chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
- if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
- {
- close(newfd);
- fclose(fp);
- continue;
- }
- retfp = fdopen(newfd, "r");
- fclose(fp);
- break;
- }
- }
- dataiterator_free(&di);
- solv_free(matchname);
- return retfp;
-}
-#endif
-
-
-#define MODE_LIST 0
-#define MODE_INSTALL 1
-#define MODE_ERASE 2
-#define MODE_UPDATE 3
-#define MODE_DISTUPGRADE 4
-#define MODE_VERIFY 5
-#define MODE_PATCH 6
-#define MODE_INFO 7
-#define MODE_REPOLIST 8
-#define MODE_SEARCH 9
-
-void
-usage(int r)
-{
- fprintf(stderr, "Usage: solv COMMAND <select>\n");
- fprintf(stderr, "\n");
- fprintf(stderr, " dist-upgrade: replace installed packages with\n");
- fprintf(stderr, " versions from the repositories\n");
- fprintf(stderr, " erase: erase installed packages\n");
- fprintf(stderr, " info: display package information\n");
- fprintf(stderr, " install: install packages\n");
- fprintf(stderr, " list: list packages\n");
- fprintf(stderr, " repos: list enabled repositories\n");
- fprintf(stderr, " search: search name/summary/description\n");
- fprintf(stderr, " update: update installed packages\n");
- fprintf(stderr, " verify: check dependencies of installed packages\n");
-#if defined(SUSE) || defined(FEDORA)
- fprintf(stderr, " patch: install newest patches\n");
-#endif
- fprintf(stderr, "\n");
- exit(r);
-}
-
-int
-main(int argc, char **argv)
-{
- Pool *pool;
- Repo *commandlinerepo = 0;
- Id *commandlinepkgs = 0;
- Id p;
- struct repoinfo *repoinfos;
- int nrepoinfos = 0;
- int mainmode = 0, mode = 0;
- int i, newpkgs;
- Queue job, checkq;
- Solver *solv = 0;
- Transaction *trans;
- FILE **newpkgsfps;
- Queue repofilter;
- Queue kindfilter;
- Queue archfilter;
- int archfilter_src = 0;
- int cleandeps = 0;
- int forcebest = 0;
- char *rootdir = 0;
- char *keyname = 0;
- int debuglevel = 0;
-
- argc--;
- argv++;
- userhome = getenv("HOME");
- if (userhome && userhome[0] != '/')
- userhome = 0;
- while (argc && !strcmp(argv[0], "-d"))
- {
- debuglevel++;
- argc--;
- argv++;
- }
- if (!argv[0])
- usage(1);
- if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
- {
- mainmode = MODE_INSTALL;
- mode = SOLVER_INSTALL;
- }
-#if defined(SUSE) || defined(FEDORA)
- else if (!strcmp(argv[0], "patch"))
- {
- mainmode = MODE_PATCH;
- mode = SOLVER_INSTALL;
- }
-#endif
- else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
- {
- mainmode = MODE_ERASE;
- mode = SOLVER_ERASE;
- }
- else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
- {
- mainmode = MODE_LIST;
- mode = 0;
- }
- else if (!strcmp(argv[0], "info"))
- {
- mainmode = MODE_INFO;
- mode = 0;
- }
- else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
- {
- mainmode = MODE_SEARCH;
- mode = 0;
- }
- else if (!strcmp(argv[0], "verify"))
- {
- mainmode = MODE_VERIFY;
- mode = SOLVER_VERIFY;
- }
- else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
- {
- mainmode = MODE_UPDATE;
- mode = SOLVER_UPDATE;
- }
- else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
- {
- mainmode = MODE_DISTUPGRADE;
- mode = SOLVER_DISTUPGRADE;
- }
- else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
- {
- mainmode = MODE_REPOLIST;
- mode = 0;
- }
- else
- usage(1);
-
- for (;;)
- {
- if (argc > 2 && !strcmp(argv[1], "--root"))
- {
- rootdir = argv[2];
- argc -= 2;
- argv += 2;
- }
- else if (argc > 1 && !strcmp(argv[1], "--clean"))
- {
- cleandeps = 1;
- argc--;
- argv++;
- }
- else if (argc > 1 && !strcmp(argv[1], "--best"))
- {
- forcebest = 1;
- argc--;
- argv++;
- }
- if (argc > 2 && !strcmp(argv[1], "--keyname"))
- {
- keyname = argv[2];
- argc -= 2;
- argv += 2;
- }
- else
- break;
- }
-
- pool = pool_create();
- pool_set_rootdir(pool, rootdir);
-
-#if 0
- {
- const char *langs[] = {"de_DE", "de", "en"};
- pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
- }
-#endif
-
- pool_setloadcallback(pool, load_stub, 0);
-#ifdef SUSE
- pool->nscallback = nscallback;
-#endif
- if (debuglevel)
- pool_setdebuglevel(pool, debuglevel);
- setarch(pool);
- pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
- repoinfos = read_repoinfos(pool, &nrepoinfos);
-
- if (mainmode == MODE_REPOLIST)
- {
- int j = 1;
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- if (!cinfo->enabled)
- continue;
- printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
- }
- exit(0);
- }
-
- read_repos(pool, repoinfos, nrepoinfos);
-
- /* setup filters */
- queue_init(&repofilter);
- queue_init(&kindfilter);
- queue_init(&archfilter);
- while (argc > 1)
- {
- if (!strcmp(argv[1], "-i"))
- {
- queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
- argc--;
- argv++;
- }
- else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
- {
- const char *rname = argv[2], *rp;
- Id repoid = 0;
- for (rp = rname; *rp; rp++)
- if (*rp <= '0' || *rp >= '9')
- break;
- if (!*rp)
- {
- /* repo specified by number */
- int rnum = atoi(rname);
- for (i = 0; i < nrepoinfos; i++)
- {
- struct repoinfo *cinfo = repoinfos + i;
- if (!cinfo->enabled)
- continue;
- if (--rnum == 0)
- repoid = cinfo->repo->repoid;
- }
- }
- else
- {
- /* repo specified by alias */
- Repo *repo;
- FOR_REPOS(i, repo)
- {
- if (!strcasecmp(rname, repo->name))
- repoid = repo->repoid;
- }
- }
- if (!repoid)
- {
- fprintf(stderr, "%s: no such repo\n", rname);
- exit(1);
- }
- /* SETVENDOR is actually wrong but useful */
- queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
- argc -= 2;
- argv += 2;
- }
- else if (argc > 2 && !strcmp(argv[1], "--arch"))
- {
- if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
- archfilter_src = 1;
- queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
- argc -= 2;
- argv += 2;
- }
- else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
- {
- const char *kind = argv[2];
- if (!strcmp(kind, "srcpackage"))
- {
- /* hey! should use --arch! */
- queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
- archfilter_src = 1;
- argc -= 2;
- argv += 2;
- continue;
- }
- if (!strcmp(kind, "package"))
- kind = "";
- if (!strcmp(kind, "all"))
- queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
- else
- queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
- argc -= 2;
- argv += 2;
- }
- else
- break;
- }
-
- if (mainmode == MODE_SEARCH)
- {
- Queue sel, q;
- Dataiterator di;
- if (argc != 2)
- usage(1);
- pool_createwhatprovides(pool);
- queue_init(&sel);
- dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
- dataiterator_set_keyname(&di, SOLVABLE_NAME);
- dataiterator_set_search(&di, 0, 0);
- while (dataiterator_step(&di))
- queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
- dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
- dataiterator_set_search(&di, 0, 0);
- while (dataiterator_step(&di))
- queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
- dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
- dataiterator_set_search(&di, 0, 0);
- while (dataiterator_step(&di))
- queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
- dataiterator_free(&di);
- if (repofilter.count)
- selection_filter(pool, &sel, &repofilter);
-
- queue_init(&q);
- selection_solvables(pool, &sel, &q);
- queue_free(&sel);
- for (i = 0; i < q.count; i++)
- {
- Solvable *s = pool_id2solvable(pool, q.elements[i]);
- printf(" - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
- }
- queue_free(&q);
- exit(0);
- }
-
- /* process command line packages */
- if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
- {
- for (i = 1; i < argc; i++)
- {
- int l;
- l = strlen(argv[i]);
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
- continue;
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
- continue;
-#endif
- if (access(argv[i], R_OK))
- {
- perror(argv[i]);
- exit(1);
- }
- if (!commandlinepkgs)
- commandlinepkgs = solv_calloc(argc, sizeof(Id));
- if (!commandlinerepo)
- commandlinerepo = repo_create(pool, "@commandline");
- p = 0;
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- p = repo_add_rpm(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- p = repo_add_deb(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
-#endif
- if (!p)
- {
- fprintf(stderr, "could not add '%s'\n", argv[i]);
- exit(1);
- }
- commandlinepkgs[i] = p;
- }
- if (commandlinerepo)
- repo_internalize(commandlinerepo);
- }
-
- // FOR_REPOS(i, repo)
- // printf("%s: %d solvables\n", repo->name, repo->nsolvables);
-
-#if defined(ENABLE_RPMDB)
- if (pool->disttype == DISTTYPE_RPM)
- addfileprovides(pool);
-#endif
-#ifdef SUSE
- add_autopackages(pool);
-#endif
- pool_createwhatprovides(pool);
-
- if (keyname)
- keyname = solv_dupjoin("solvable:", keyname, 0);
- queue_init(&job);
- for (i = 1; i < argc; i++)
- {
- Queue job2;
- int j, flags, rflags;
-
- if (commandlinepkgs && commandlinepkgs[i])
- {
- queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
- continue;
- }
- queue_init(&job2);
- flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
- flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
- if (kindfilter.count)
- flags |= SELECTION_SKIP_KIND;
- if (mode == MODE_LIST || archfilter_src)
- flags |= SELECTION_WITH_SOURCE;
- if (argv[i][0] == '/')
- flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
- if (!keyname)
- rflags = selection_make(pool, &job2, argv[i], flags);
- else
- rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
- if (repofilter.count)
- selection_filter(pool, &job2, &repofilter);
- if (archfilter.count)
- selection_filter(pool, &job2, &archfilter);
- if (kindfilter.count)
- selection_filter(pool, &job2, &kindfilter);
- if (!job2.count)
- {
- flags |= SELECTION_NOCASE;
- if (!keyname)
- rflags = selection_make(pool, &job2, argv[i], flags);
- else
- rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
- if (repofilter.count)
- selection_filter(pool, &job2, &repofilter);
- if (archfilter.count)
- selection_filter(pool, &job2, &archfilter);
- if (kindfilter.count)
- selection_filter(pool, &job2, &kindfilter);
- if (job2.count)
- printf("[ignoring case for '%s']\n", argv[i]);
- }
- if (!job2.count)
- {
- fprintf(stderr, "nothing matches '%s'\n", argv[i]);
- exit(1);
- }
- if (rflags & SELECTION_FILELIST)
- printf("[using file list match for '%s']\n", argv[i]);
- if (rflags & SELECTION_PROVIDES)
- printf("[using capability match for '%s']\n", argv[i]);
- for (j = 0; j < job2.count; j++)
- queue_push(&job, job2.elements[j]);
- queue_free(&job2);
- }
- keyname = solv_free(keyname);
-
- if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
- {
- queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
- if (repofilter.count)
- selection_filter(pool, &job, &repofilter);
- if (archfilter.count)
- selection_filter(pool, &job, &archfilter);
- if (kindfilter.count)
- selection_filter(pool, &job, &kindfilter);
- }
- queue_free(&repofilter);
- queue_free(&archfilter);
- queue_free(&kindfilter);
-
- if (!job.count && mainmode != MODE_PATCH)
- {
- printf("no package matched\n");
- exit(1);
- }
-
- if (mainmode == MODE_LIST || mainmode == MODE_INFO)
- {
- /* list mode, no solver needed */
- Queue q;
- queue_init(&q);
- for (i = 0; i < job.count; i += 2)
- {
- int j;
- queue_empty(&q);
- pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
- for (j = 0; j < q.count; j++)
- {
- Solvable *s = pool_id2solvable(pool, q.elements[j]);
- if (mainmode == MODE_INFO)
- {
- const char *str;
- printf("Name: %s\n", pool_solvable2str(pool, s));
- printf("Repo: %s\n", s->repo->name);
- printf("Summary: %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
- str = solvable_lookup_str(s, SOLVABLE_URL);
- if (str)
- printf("Url: %s\n", str);
- str = solvable_lookup_str(s, SOLVABLE_LICENSE);
- if (str)
- printf("License: %s\n", str);
-#if 0
- str = solvable_lookup_sourcepkg(s);
- if (str)
- printf("Source: %s\n", str);
-#endif
- printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
- printf("\n");
- }
- else
- {
-#if 1
- const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
-#else
- const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
-#endif
- printf(" - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
- if (sum)
- printf(" %s\n", sum);
- }
- }
- }
- queue_free(&q);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
-#ifdef FEDORA
- yum_substitute(pool, 0);
-#endif
- exit(0);
- }
-
-#if defined(SUSE) || defined(FEDORA)
- if (mainmode == MODE_PATCH)
- add_patchjobs(pool, &job);
-#endif
-
- // add mode
- for (i = 0; i < job.count; i += 2)
- {
- job.elements[i] |= mode;
- if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
- job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
- if (cleandeps)
- job.elements[i] |= SOLVER_CLEANDEPS;
- if (forcebest)
- job.elements[i] |= SOLVER_FORCEBEST;
- }
-
- // multiversion test
- // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
- // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
- // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
-#if 0
- queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
- queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
-#endif
-
-#ifdef SOFTLOCKS_PATH
- addsoftlocks(pool, &job);
-#endif
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-rerunsolver:
-#endif
- solv = solver_create(pool);
- solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
-#ifdef FEDORA
- solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
-#endif
- if (mainmode == MODE_ERASE)
- solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1); /* don't nag */
- solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
-
- for (;;)
- {
- Id problem, solution;
- int pcnt, scnt;
-
- if (!solver_solve(solv, &job))
- break;
- pcnt = solver_problem_count(solv);
- printf("Found %d problems:\n", pcnt);
- for (problem = 1; problem <= pcnt; problem++)
- {
- int take = 0;
- printf("Problem %d/%d:\n", problem, pcnt);
- solver_printprobleminfo(solv, problem);
- printf("\n");
- scnt = solver_solution_count(solv, problem);
- for (solution = 1; solution <= scnt; solution++)
- {
- printf("Solution %d:\n", solution);
- solver_printsolution(solv, problem, solution);
- printf("\n");
- }
- for (;;)
- {
- char inbuf[128], *ip;
- printf("Please choose a solution: ");
- fflush(stdout);
- *inbuf = 0;
- if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
- {
- printf("Abort.\n");
- exit(1);
- }
- while (*ip == ' ' || *ip == '\t')
- ip++;
- if (*ip >= '0' && *ip <= '9')
- {
- take = atoi(ip);
- if (take >= 1 && take <= scnt)
- break;
- }
- if (*ip == 's')
- {
- take = 0;
- break;
- }
- if (*ip == 'q')
- {
- printf("Abort.\n");
- exit(1);
- }
- }
- if (!take)
- continue;
- solver_take_solution(solv, problem, take, &job);
- }
- }
-
- trans = solver_create_transaction(solv);
- if (!trans->steps.count)
- {
- printf("Nothing to do.\n");
- transaction_free(trans);
- solver_free(solv);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
-#ifdef FEDORA
- yum_substitute(pool, 0);
-#endif
- exit(1);
- }
-
- /* display transaction to the user and ask for confirmation */
- printf("\n");
- printf("Transaction summary:\n\n");
- transaction_print(trans);
-#if defined(SUSE)
- showdiskusagechanges(trans);
-#endif
- printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
- printf("\n");
-
- if (!yesno("OK to continue (y/n)? "))
- {
- printf("Abort.\n");
- transaction_free(trans);
- solver_free(solv);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
-#ifdef FEDORA
- yum_substitute(pool, 0);
-#endif
- exit(1);
- }
-
- /* download all new packages */
- queue_init(&checkq);
- newpkgs = transaction_installedresult(trans, &checkq);
- newpkgsfps = 0;
- if (newpkgs)
- {
- int downloadsize = 0;
- for (i = 0; i < newpkgs; i++)
- {
- Solvable *s;
-
- p = checkq.elements[i];
- s = pool_id2solvable(pool, p);
- downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
- }
- printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
- newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
- for (i = 0; i < newpkgs; i++)
- {
- unsigned int medianr;
- const char *loc;
- Solvable *s;
- struct repoinfo *cinfo;
- const unsigned char *chksum;
- Id chksumtype;
-
- p = checkq.elements[i];
- s = pool_id2solvable(pool, p);
- if (s->repo == commandlinerepo)
- {
- loc = solvable_lookup_location(s, &medianr);
- if (!loc)
- continue;
- if (!(newpkgsfps[i] = fopen(loc, "r")))
- {
- perror(loc);
- exit(1);
- }
- putchar('.');
- continue;
- }
- cinfo = s->repo->appdata;
- if (!cinfo)
- {
- printf("%s: no repository information\n", s->repo->name);
- exit(1);
- }
- loc = solvable_lookup_location(s, &medianr);
- if (!loc)
- continue;
-#if defined(ENABLE_RPMDB)
- if (pool->installed && pool->installed->nsolvables)
- {
- if ((newpkgsfps[i] = trydeltadownload(s, cinfo, loc)) != 0)
- {
- putchar('d');
- fflush(stdout);
- continue; /* delta worked! */
- }
- }
-#endif
-#ifdef ENABLE_SUSEREPO
- if (cinfo->type == TYPE_SUSETAGS)
- {
- const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
- loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
- }
-#endif
- chksumtype = 0;
- chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
- if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
- {
- printf("\n%s: %s not found in repository\n", s->repo->name, loc);
- exit(1);
- }
- putchar('.');
- fflush(stdout);
- }
- putchar('\n');
- }
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- /* check for file conflicts */
- if (newpkgs)
- {
- Queue conflicts;
- struct fcstate fcstate;
-
- printf("Searching for file conflicts\n");
- queue_init(&conflicts);
- fcstate.rpmstate = rpm_state_create(pool, rootdir);
- fcstate.newpkgscnt = newpkgs;
- fcstate.checkq = &checkq;
- fcstate.newpkgsfps = newpkgsfps;
- pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
- fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
- if (conflicts.count)
- {
- printf("\n");
- for (i = 0; i < conflicts.count; i += 6)
- 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 + 4]));
- printf("\n");
- if (yesno("Re-run solver (y/n/q)? "))
- {
- for (i = 0; i < newpkgs; i++)
- if (newpkgsfps[i])
- fclose(newpkgsfps[i]);
- newpkgsfps = solv_free(newpkgsfps);
- solver_free(solv);
- solv = 0;
- pool_add_fileconflicts_deps(pool, &conflicts);
- goto rerunsolver;
- }
- }
- queue_free(&conflicts);
- }
-#endif
-
- /* and finally commit the transaction */
- printf("Committing transaction:\n\n");
- transaction_order(trans, 0);
- for (i = 0; i < trans->steps.count; i++)
- {
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- const char *evr, *evrp, *nvra;
-#endif
- Solvable *s;
- int j;
- FILE *fp;
- Id type;
-
- p = trans->steps.elements[i];
- s = pool_id2solvable(pool, p);
- type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
- switch(type)
- {
- case SOLVER_TRANSACTION_ERASE:
- printf("erase %s\n", pool_solvid2str(pool, p));
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
- continue;
- /* strip epoch from evr */
- evr = evrp = pool_id2str(pool, s->evr);
- while (*evrp >= '0' && *evrp <= '9')
- evrp++;
- if (evrp > evr && evrp[0] == ':' && evrp[1])
- evr = evrp + 1;
- nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
- nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
- runrpm("-e", nvra, -1, rootdir); /* too bad that --querybynumber doesn't work */
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
-#endif
- break;
- case SOLVER_TRANSACTION_INSTALL:
- case SOLVER_TRANSACTION_MULTIINSTALL:
- printf("install %s\n", pool_solvid2str(pool, p));
- for (j = 0; j < newpkgs; j++)
- if (checkq.elements[j] == p)
- break;
- fp = j < newpkgs ? newpkgsfps[j] : 0;
- if (!fp)
- continue;
- rewind(fp);
- lseek(fileno(fp), 0, SEEK_SET);
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
- runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
- rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
-#endif
- fclose(fp);
- newpkgsfps[j] = 0;
- break;
- default:
- break;
- }
- }
-
- for (i = 0; i < newpkgs; i++)
- if (newpkgsfps[i])
- fclose(newpkgsfps[i]);
- solv_free(newpkgsfps);
- queue_free(&checkq);
- transaction_free(trans);
- solver_free(solv);
- queue_free(&job);
- pool_free(pool);
- free_repoinfos(repoinfos, nrepoinfos);
- solv_free(commandlinepkgs);
-#ifdef FEDORA
- yum_substitute(pool, 0);
-#endif
- exit(0);
-}
diff --git a/examples/solv/CMakeLists.txt b/examples/solv/CMakeLists.txt
new file mode 100644
index 0000000..41f45f7
--- /dev/null
+++ b/examples/solv/CMakeLists.txt
@@ -0,0 +1,29 @@
+
+ADD_EXECUTABLE (solv solv.c
+checksig.c
+deltarpm.c
+fastestmirror.c
+fileconflicts.c
+fileprovides.c
+mirror.c
+patchjobs.c
+repoinfo.c
+repoinfo_cache.c
+repoinfo_config_debian.c
+repoinfo_config_yum.c
+repoinfo_config_urpmi.c
+repoinfo_download.c
+repoinfo_system_debian.c
+repoinfo_system_rpm.c
+repoinfo_type_debian.c
+repoinfo_type_mdk.c
+repoinfo_type_rpmmd.c
+repoinfo_type_susetags.c
+)
+
+TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL(TARGETS
+ solv
+ DESTINATION ${BIN_INSTALL_DIR})
+
diff --git a/examples/solv/checksig.c b/examples/solv/checksig.c
new file mode 100644
index 0000000..ff60c66
--- /dev/null
+++ b/examples/solv/checksig.c
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "pool.h"
+#include "repo.h"
+#ifdef ENABLE_PUBKEY
+#include "repo_pubkey.h"
+#endif
+
+#include "checksig.h"
+
+#ifndef DEBIAN
+
+static void
+cleanupgpg(char *gpgdir)
+{
+ char cmd[256];
+ snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
+ unlink(cmd);
+ snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
+ unlink(cmd);
+ rmdir(gpgdir);
+}
+
+int
+checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
+{
+ char *gpgdir;
+ char *keysfile;
+ const char *pubkey;
+ char cmd[256];
+ FILE *kfp;
+ Solvable *s;
+ Id p;
+ off_t posfp, possigfp;
+ int r, nkeys;
+
+ gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
+ if (!gpgdir)
+ return 0;
+ keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
+ if (!(kfp = fopen(keysfile, "w")) )
+ {
+ cleanupgpg(gpgdir);
+ return 0;
+ }
+ nkeys = 0;
+ for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
+ {
+ if (!s->repo)
+ continue;
+ pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
+ if (!pubkey || !*pubkey)
+ continue;
+ if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
+ break;
+ if (fputc('\n', kfp) == EOF) /* Just in case... */
+ break;
+ nkeys++;
+ }
+ if (fclose(kfp) || !nkeys || p < sigpool->nsolvables)
+ {
+ cleanupgpg(gpgdir);
+ return 0;
+ }
+ snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
+ if (system(cmd))
+ {
+ fprintf(stderr, "key import error\n");
+ cleanupgpg(gpgdir);
+ return 0;
+ }
+ unlink(keysfile);
+ posfp = lseek(fileno(fp), 0, SEEK_CUR);
+ lseek(fileno(fp), 0, SEEK_SET);
+ possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
+ lseek(fileno(sigfp), 0, SEEK_SET);
+ snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
+ fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
+ fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
+ r = system(cmd);
+ lseek(fileno(sigfp), possigfp, SEEK_SET);
+ lseek(fileno(fp), posfp, SEEK_SET);
+ fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
+ cleanupgpg(gpgdir);
+ return r == 0 ? 1 : 0;
+}
+
+#else
+
+int
+checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
+{
+ char cmd[256];
+ int r;
+
+ 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));
+ fcntl(fileno(fp), F_SETFD, 0); /* clear CLOEXEC */
+ fcntl(fileno(sigfp), F_SETFD, 0); /* clear CLOEXEC */
+ r = system(cmd);
+ fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
+ return r == 0 ? 1 : 0;
+}
+
+#endif
+
+Pool *
+read_sigs()
+{
+ Pool *sigpool = pool_create();
+#if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
+ Repo *repo = repo_create(sigpool, "pubkeys");
+ repo_add_rpmdb_pubkeys(repo, 0);
+#endif
+ return sigpool;
+}
diff --git a/examples/solv/checksig.h b/examples/solv/checksig.h
new file mode 100644
index 0000000..67ebaf8
--- /dev/null
+++ b/examples/solv/checksig.h
@@ -0,0 +1,3 @@
+extern int checksig(Pool *sigpool, FILE *fp, FILE *sigfp);
+extern Pool *read_sigs();
+
diff --git a/examples/solv/deltarpm.c b/examples/solv/deltarpm.c
new file mode 100644
index 0000000..551d570
--- /dev/null
+++ b/examples/solv/deltarpm.c
@@ -0,0 +1,133 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repoinfo.h"
+#include "repoinfo_download.h"
+
+#include "deltarpm.h"
+
+static inline int
+opentmpfile()
+{
+ char tmpl[100];
+ int fd;
+
+ strcpy(tmpl, "/var/tmp/solvXXXXXX");
+ fd = mkstemp(tmpl);
+ if (fd < 0)
+ {
+ perror("mkstemp");
+ exit(1);
+ }
+ unlink(tmpl);
+ return fd;
+}
+
+FILE *
+trydeltadownload(Solvable *s, const char *loc)
+{
+ Repo *repo = s->repo;
+ Pool *pool = repo->pool;
+ struct repoinfo *cinfo = repo->appdata;
+ Dataiterator di;
+ Id pp;
+ const unsigned char *chksum;
+ Id chksumtype;
+ FILE *retfp = 0;
+ char *matchname = strdup(pool_id2str(pool, s->name));
+
+ dataiterator_init(&di, pool, repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
+ dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
+ while (dataiterator_step(&di))
+ {
+ Id baseevr, op;
+
+ dataiterator_setpos_parent(&di);
+ if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
+ pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
+ continue;
+ baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
+ FOR_PROVIDES(op, pp, s->name)
+ {
+ Solvable *os = pool->solvables + op;
+ if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
+ break;
+ }
+ if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
+ {
+ /* base is installed, run sequence check */
+ const char *seq;
+ const char *dloc;
+ const char *archstr;
+ FILE *fp;
+ char cmd[128];
+ int newfd;
+
+ archstr = pool_id2str(pool, s->arch);
+ if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
+ continue;
+
+ seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
+ seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
+ if (strchr(seq, '\'') != 0)
+ continue;
+#ifdef FEDORA
+ sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
+#else
+ sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
+#endif
+ if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
+ continue; /* didn't match */
+ /* looks good, download delta */
+ chksumtype = 0;
+ chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
+ if (!chksumtype)
+ continue; /* no way! */
+ dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
+ if (!dloc)
+ continue;
+#ifdef ENABLE_SUSEREPO
+ if (cinfo->type == TYPE_SUSETAGS)
+ {
+ const char *datadir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DATADIR);
+ dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
+ }
+#endif
+ if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
+ continue;
+ /* got it, now reconstruct */
+ newfd = opentmpfile();
+#ifdef FEDORA
+ sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
+#else
+ sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
+#endif
+ fcntl(fileno(fp), F_SETFD, 0);
+ if (system(cmd))
+ {
+ close(newfd);
+ fclose(fp);
+ continue;
+ }
+ lseek(newfd, 0, SEEK_SET);
+ chksumtype = 0;
+ chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
+ if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
+ {
+ close(newfd);
+ fclose(fp);
+ continue;
+ }
+ retfp = fdopen(newfd, "r");
+ fclose(fp);
+ break;
+ }
+ }
+ dataiterator_free(&di);
+ solv_free(matchname);
+ return retfp;
+}
diff --git a/examples/solv/deltarpm.h b/examples/solv/deltarpm.h
new file mode 100644
index 0000000..528e5a2
--- /dev/null
+++ b/examples/solv/deltarpm.h
@@ -0,0 +1 @@
+extern FILE *trydeltadownload(Solvable *s, const char *loc);
diff --git a/examples/solv/fastestmirror.c b/examples/solv/fastestmirror.c
new file mode 100644
index 0000000..d2ebd97
--- /dev/null
+++ b/examples/solv/fastestmirror.c
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+#include <errno.h>
+
+#include "util.h"
+
+#include "fastestmirror.h"
+
+void
+findfastest(char **urls, int nurls)
+{
+ int i, j, port;
+ int *socks, qc;
+ struct pollfd *fds;
+ char *p, *p2, *q;
+ char portstr[16];
+ struct addrinfo hints, *result;;
+
+ fds = solv_calloc(nurls, sizeof(*fds));
+ socks = solv_calloc(nurls, sizeof(*socks));
+ for (i = 0; i < nurls; i++)
+ {
+ socks[i] = -1;
+ p = strchr(urls[i], '/');
+ if (!p)
+ continue;
+ if (p[1] != '/')
+ continue;
+ p += 2;
+ q = strchr(p, '/');
+ qc = 0;
+ if (q)
+ {
+ qc = *q;
+ *q = 0;
+ }
+ if ((p2 = strchr(p, '@')) != 0)
+ p = p2 + 1;
+ port = 80;
+ if (!strncmp("https:", urls[i], 6))
+ port = 443;
+ else if (!strncmp("ftp:", urls[i], 4))
+ port = 21;
+ if ((p2 = strrchr(p, ':')) != 0)
+ {
+ port = atoi(p2 + 1);
+ if (q)
+ *q = qc;
+ q = p2;
+ qc = *q;
+ *q = 0;
+ }
+ sprintf(portstr, "%d", port);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICSERV;
+ result = 0;
+ if (!getaddrinfo(p, portstr, &hints, &result))
+ {
+ socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
+ if (socks[i] >= 0)
+ {
+ fcntl(socks[i], F_SETFL, O_NONBLOCK);
+ if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
+ {
+ if (errno != EINPROGRESS)
+ {
+ close(socks[i]);
+ socks[i] = -1;
+ }
+ }
+ }
+ freeaddrinfo(result);
+ }
+ if (q)
+ *q = qc;
+ }
+ for (;;)
+ {
+ for (i = j = 0; i < nurls; i++)
+ {
+ if (socks[i] < 0)
+ continue;
+ fds[j].fd = socks[i];
+ fds[j].events = POLLOUT;
+ j++;
+ }
+ if (j < 2)
+ {
+ i = j - 1;
+ break;
+ }
+ if (poll(fds, j, 10000) <= 0)
+ {
+ i = -1; /* something is wrong */
+ break;
+ }
+ for (i = 0; i < j; i++)
+ if ((fds[i].revents & POLLOUT) != 0)
+ {
+ int soe = 0;
+ socklen_t soel = sizeof(int);
+ if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
+ {
+ /* connect failed, kill socket */
+ for (j = 0; j < nurls; j++)
+ if (socks[j] == fds[i].fd)
+ {
+ close(socks[j]);
+ socks[j] = -1;
+ }
+ i = j + 1;
+ break;
+ }
+ break; /* horray! */
+ }
+ if (i == j + 1)
+ continue;
+ if (i == j)
+ i = -1; /* something is wrong, no bit was set */
+ break;
+ }
+ /* now i contains the fastest fd index */
+ if (i >= 0)
+ {
+ for (j = 0; j < nurls; j++)
+ if (socks[j] == fds[i].fd)
+ break;
+ if (j != 0)
+ {
+ char *url0 = urls[0];
+ urls[0] = urls[j];
+ urls[j] = url0;
+ }
+ }
+ for (i = j = 0; i < nurls; i++)
+ if (socks[i] >= 0)
+ close(socks[i]);
+ free(socks);
+ free(fds);
+}
diff --git a/examples/solv/fastestmirror.h b/examples/solv/fastestmirror.h
new file mode 100644
index 0000000..53251ef
--- /dev/null
+++ b/examples/solv/fastestmirror.h
@@ -0,0 +1,2 @@
+extern void findfastest(char **urls, int nurls);
+
diff --git a/examples/solv/fileconflicts.c b/examples/solv/fileconflicts.c
new file mode 100644
index 0000000..982de85
--- /dev/null
+++ b/examples/solv/fileconflicts.c
@@ -0,0 +1,76 @@
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#include "pool_fileconflicts.h"
+
+#include "fileconflicts.h"
+
+struct fcstate {
+ FILE **newpkgsfps;
+ Queue *checkq;
+ int newpkgscnt;
+ void *rpmstate;
+};
+
+static void *
+fileconflict_cb(Pool *pool, Id p, void *cbdata)
+{
+ struct fcstate *fcstate = cbdata;
+ Solvable *s;
+ Id rpmdbid;
+ int i;
+ FILE *fp;
+
+ s = pool_id2solvable(pool, p);
+ if (pool->installed && s->repo == pool->installed)
+ {
+ if (!s->repo->rpmdbid)
+ return 0;
+ rpmdbid = s->repo->rpmdbid[p - s->repo->start];
+ if (!rpmdbid)
+ return 0;
+ return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
+ }
+ for (i = 0; i < fcstate->newpkgscnt; i++)
+ if (fcstate->checkq->elements[i] == p)
+ break;
+ if (i == fcstate->newpkgscnt)
+ return 0;
+ fp = fcstate->newpkgsfps[i];
+ if (!fp)
+ return 0;
+ rewind(fp);
+ return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
+}
+
+int
+checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts)
+{
+ struct fcstate fcstate;
+ int i;
+
+ printf("Searching for file conflicts\n");
+ queue_init(conflicts);
+ fcstate.rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+ fcstate.newpkgscnt = newpkgs;
+ fcstate.checkq = checkq;
+ fcstate.newpkgsfps = newpkgsfps;
+ pool_findfileconflicts(pool, checkq, newpkgs, conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
+ fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
+ if (conflicts->count)
+ {
+ printf("\n");
+ for (i = 0; i < conflicts->count; i += 6)
+ 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 + 4]));
+ printf("\n");
+ }
+ return conflicts->count;
+}
+
+#endif
diff --git a/examples/solv/fileconflicts.h b/examples/solv/fileconflicts.h
new file mode 100644
index 0000000..e3f31e7
--- /dev/null
+++ b/examples/solv/fileconflicts.h
@@ -0,0 +1,2 @@
+extern int checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts);
+
diff --git a/examples/solv/fileprovides.c b/examples/solv/fileprovides.c
new file mode 100644
index 0000000..2654ab6
--- /dev/null
+++ b/examples/solv/fileprovides.c
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#include "fileprovides.h"
+
+static void
+rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
+{
+ Repo *repo;
+ Repodata *data;
+ Map providedids;
+ Queue fileprovidesq;
+ int i, j, n;
+ struct repoinfo *cinfo;
+
+ map_init(&providedids, pool->ss.nstrings);
+ queue_init(&fileprovidesq);
+ for (i = 0; i < addedfileprovides->count; i++)
+ MAPSET(&providedids, addedfileprovides->elements[i]);
+ FOR_REPOS(i, repo)
+ {
+ /* make sure all repodatas but the first are extensions */
+ if (repo->nrepodata < 2)
+ continue;
+ cinfo = repo->appdata;
+ if (!cinfo)
+ continue; /* cmdline */
+ if (cinfo->incomplete)
+ continue;
+ data = repo_id2repodata(repo, 1);
+ if (data->loadcallback)
+ continue;
+ for (j = 2; j < repo->nrepodata; j++)
+ {
+ Repodata *edata = repo_id2repodata(repo, j);
+ if (!edata->loadcallback)
+ break;
+ }
+ if (j < repo->nrepodata)
+ continue; /* found a non-extension repodata, can't rewrite */
+ if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
+ {
+ if (repo == pool->installed && addedfileprovides_inst)
+ {
+ for (j = 0; j < addedfileprovides->count; j++)
+ MAPCLR(&providedids, addedfileprovides->elements[j]);
+ for (j = 0; j < addedfileprovides_inst->count; j++)
+ MAPSET(&providedids, addedfileprovides_inst->elements[j]);
+ }
+ n = 0;
+ for (j = 0; j < fileprovidesq.count; j++)
+ if (MAPTST(&providedids, fileprovidesq.elements[j]))
+ n++;
+ if (repo == pool->installed && addedfileprovides_inst)
+ {
+ for (j = 0; j < addedfileprovides_inst->count; j++)
+ MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
+ for (j = 0; j < addedfileprovides->count; j++)
+ MAPSET(&providedids, addedfileprovides->elements[j]);
+ if (n == addedfileprovides_inst->count)
+ continue; /* nothing new added */
+ }
+ else if (n == addedfileprovides->count)
+ continue; /* nothing new added */
+ }
+ repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, data);
+ }
+ queue_free(&fileprovidesq);
+ map_free(&providedids);
+}
+
+void
+addfileprovides(Pool *pool)
+{
+ Queue addedfileprovides;
+ Queue addedfileprovides_inst;
+
+ queue_init(&addedfileprovides);
+ queue_init(&addedfileprovides_inst);
+ pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
+ if (addedfileprovides.count || addedfileprovides_inst.count)
+ rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
+ queue_free(&addedfileprovides);
+ queue_free(&addedfileprovides_inst);
+}
diff --git a/examples/solv/fileprovides.h b/examples/solv/fileprovides.h
new file mode 100644
index 0000000..1069d5a
--- /dev/null
+++ b/examples/solv/fileprovides.h
@@ -0,0 +1 @@
+void addfileprovides(Pool *pool);
diff --git a/examples/solv/mirror.c b/examples/solv/mirror.c
new file mode 100644
index 0000000..52dc5ef
--- /dev/null
+++ b/examples/solv/mirror.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "util.h"
+#include "fastestmirror.h"
+
+#include "mirror.h"
+
+char *
+findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
+{
+ char buf[4096], *bp, *ep;
+ char **urls = 0;
+ int nurls = 0;
+ int i;
+
+ if (chksumtypep)
+ *chksumtypep = 0;
+ while((bp = fgets(buf, sizeof(buf), fp)) != 0)
+ {
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
+ {
+ bp += 20;
+ if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
+ *chksumtypep = REPOKEY_TYPE_SHA256;
+ continue;
+ }
+ if (strncmp(bp, "<url", 4))
+ continue;
+ bp = strchr(bp, '>');
+ if (!bp)
+ continue;
+ bp++;
+ ep = strstr(bp, "repodata/repomd.xml</url>");
+ if (!ep)
+ continue;
+ *ep = 0;
+ if (strncmp(bp, "http", 4))
+ continue;
+ urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
+ urls[nurls++] = strdup(bp);
+ }
+ if (nurls)
+ {
+ if (nurls > 1)
+ findfastest(urls, nurls > 5 ? 5 : nurls);
+ bp = urls[0];
+ urls[0] = 0;
+ for (i = 0; i < nurls; i++)
+ solv_free(urls[i]);
+ solv_free(urls);
+ ep = strchr(bp, '/');
+ if ((ep = strchr(ep + 2, '/')) != 0)
+ {
+ *ep = 0;
+ printf("[using mirror %s]\n", bp);
+ *ep = '/';
+ }
+ return bp;
+ }
+ return 0;
+}
+
+char *
+findmirrorlisturl(FILE *fp)
+{
+ char buf[4096], *bp, *ep;
+ int i, l;
+ char **urls = 0;
+ int nurls = 0;
+
+ while((bp = fgets(buf, sizeof(buf), fp)) != 0)
+ {
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ if (!*bp || *bp == '#')
+ continue;
+ l = strlen(bp);
+ while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
+ bp[--l] = 0;
+ if ((ep = strstr(bp, "url=")) != 0)
+ bp = ep + 4;
+ urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
+ urls[nurls++] = strdup(bp);
+ }
+ if (nurls)
+ {
+ if (nurls > 1)
+ findfastest(urls, nurls > 5 ? 5 : nurls);
+ bp = urls[0];
+ urls[0] = 0;
+ for (i = 0; i < nurls; i++)
+ solv_free(urls[i]);
+ solv_free(urls);
+ ep = strchr(bp, '/');
+ if ((ep = strchr(ep + 2, '/')) != 0)
+ {
+ *ep = 0;
+ printf("[using mirror %s]\n", bp);
+ *ep = '/';
+ }
+ return bp;
+ }
+ return 0;
+}
diff --git a/examples/solv/mirror.h b/examples/solv/mirror.h
new file mode 100644
index 0000000..ed85a39
--- /dev/null
+++ b/examples/solv/mirror.h
@@ -0,0 +1,2 @@
+char *findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep);
+char *findmirrorlisturl(FILE *fp);
diff --git a/examples/solv/patchjobs.c b/examples/solv/patchjobs.c
new file mode 100644
index 0000000..64ad607
--- /dev/null
+++ b/examples/solv/patchjobs.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "evr.h"
+#include "solver.h"
+
+void
+add_patchjobs(Pool *pool, Queue *job)
+{
+ Id p, pp;
+ int pruneyou = 0;
+ Map installedmap, multiversionmap;
+ Solvable *s;
+
+ map_init(&multiversionmap, 0);
+ map_init(&installedmap, pool->nsolvables);
+ solver_calculate_multiversionmap(pool, job, &multiversionmap);
+ if (pool->installed)
+ FOR_REPO_SOLVABLES(pool->installed, p, s)
+ MAPSET(&installedmap, p);
+
+ /* install all patches */
+ for (p = 1; p < pool->nsolvables; p++)
+ {
+ const char *type;
+ int r;
+ Id p2;
+
+ s = pool->solvables + p;
+ if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
+ continue;
+ FOR_PROVIDES(p2, pp, s->name)
+ {
+ Solvable *s2 = pool->solvables + p2;
+ if (s2->name != s->name)
+ continue;
+ r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
+ if (r < 0 || (r == 0 && p > p2))
+ break;
+ }
+ if (p2)
+ continue;
+ type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
+ if (type && !strcmp(type, "optional"))
+ continue;
+ r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
+ if (r == -1)
+ continue;
+ if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
+ {
+ if (!pruneyou++)
+ queue_empty(job);
+ }
+ else if (pruneyou)
+ continue;
+ queue_push2(job, SOLVER_SOLVABLE, p);
+ }
+ map_free(&installedmap);
+ map_free(&multiversionmap);
+}
diff --git a/examples/solv/patchjobs.h b/examples/solv/patchjobs.h
new file mode 100644
index 0000000..52edcbe
--- /dev/null
+++ b/examples/solv/patchjobs.h
@@ -0,0 +1,2 @@
+extern void add_patchjobs(Pool *pool, Queue *job);
+
diff --git a/examples/solv/repoinfo.c b/examples/solv/repoinfo.c
new file mode 100644
index 0000000..e08d160
--- /dev/null
+++ b/examples/solv/repoinfo.c
@@ -0,0 +1,275 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+#include "repo_rpmdb.h"
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+#include "repo_deb.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#if defined(SUSE) || defined(FEDORA)
+#include "repoinfo_config_yum.h"
+#endif
+#if defined(DEBIAN)
+#include "repoinfo_config_debian.h"
+#endif
+#if defined(MANDRIVA) || defined(MAGEIA)
+#include "repoinfo_config_urpmi.h"
+#endif
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+#include "repoinfo_system_rpm.h"
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+#include "repoinfo_system_debian.h"
+#endif
+
+#ifdef ENABLE_RPMMD
+#include "repoinfo_type_rpmmd.h"
+#endif
+#ifdef ENABLE_SUSEREPO
+#include "repoinfo_type_susetags.h"
+#endif
+#ifdef ENABLE_DEBIAN
+#include "repoinfo_type_debian.h"
+#endif
+#ifdef ENABLE_MDKREPO
+#include "repoinfo_type_mdk.h"
+#endif
+
+static int
+repoinfos_sort_cmp(const void *ap, const void *bp)
+{
+ const struct repoinfo *a = ap;
+ const struct repoinfo *b = bp;
+ return strcmp(a->alias, b->alias);
+}
+
+void
+sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
+{
+ qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), repoinfos_sort_cmp);
+}
+
+void
+free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
+{
+ int i, j;
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ solv_free(cinfo->name);
+ solv_free(cinfo->alias);
+ solv_free(cinfo->path);
+ solv_free(cinfo->metalink);
+ solv_free(cinfo->mirrorlist);
+ solv_free(cinfo->baseurl);
+ for (j = 0; j < cinfo->ncomponents; j++)
+ solv_free(cinfo->components[j]);
+ solv_free(cinfo->components);
+ }
+ solv_free(repoinfos);
+#if defined(SUSE) || defined(FEDORA)
+ yum_substitute((Pool *)0, 0); /* free data */
+#endif
+}
+
+struct repoinfo *
+read_repoinfos(Pool *pool, int *nrepoinfosp)
+{
+ struct repoinfo *repoinfos = 0;
+#if defined(SUSE) || defined(FEDORA)
+ repoinfos = read_repoinfos_yum(pool, nrepoinfosp);
+#endif
+#if defined(MANDRIVA) || defined(MAGEIA)
+ repoinfos = read_repoinfos_urpmi(pool, nrepoinfosp);
+#endif
+#if defined(DEBIAN)
+ repoinfos = read_repoinfos_debian(pool, nrepoinfosp);
+#endif
+ return repoinfos;
+}
+
+int
+read_installed_repo(struct repoinfo *cinfo, Pool *pool)
+{
+ int r = 1;
+ cinfo->type = TYPE_INSTALLED;
+ cinfo->repo = repo_create(pool, "@System");
+ cinfo->repo->appdata = cinfo;
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ r = read_installed_rpm(cinfo);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ r = read_installed_debian(cinfo);
+#endif
+ pool_set_installed(pool, cinfo->repo);
+ return r;
+}
+
+int
+is_cmdline_package(const char *filename)
+{
+ int l = strlen(filename);
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ if (l > 4 && !strcmp(filename + l - 4, ".rpm"))
+ return 1;
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ if (l > 4 && !strcmp(filename + l - 4, ".deb"))
+ return 1;
+#endif
+ return 0;
+}
+
+Id
+add_cmdline_package(Repo *repo, const char *filename)
+{
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ return repo_add_rpm(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ return repo_add_deb(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
+#endif
+ return 0;
+}
+
+void
+commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp)
+{
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ commit_transactionelement_rpm(pool, type, p, fp);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+ commit_transactionelement_debian(pool, type, p, fp);
+#endif
+}
+
+void
+add_ext_keys(Repodata *data, Id handle, const char *ext)
+{
+ static Id langtags[] = {
+ SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
+ SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
+ SOLVABLE_EULA, REPOKEY_TYPE_STR,
+ SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
+ SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
+ SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
+ 0, 0
+ };
+ if (!strcmp(ext, "DL"))
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
+ }
+ else if (!strcmp(ext, "FL"))
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
+ }
+ else if (!strcmp(ext, "DU"))
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
+ }
+ else
+ {
+ Pool *pool = data->repo->pool;
+ int i;
+ for (i = 0; langtags[i]; i += 2)
+ {
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, langtags[i], ext, 1));
+ repodata_add_idarray(data, handle, REPOSITORY_KEYS, langtags[i + 1]);
+ }
+ }
+}
+
+int
+load_stub(Pool *pool, Repodata *data, void *dp)
+{
+ struct repoinfo *cinfo = data->repo->appdata;
+ switch (cinfo->type)
+ {
+#ifdef ENABLE_SUSEREPO
+ case TYPE_SUSETAGS:
+ return susetags_load_ext(data->repo, data);
+#endif
+#ifdef ENABLE_RPMMD
+ case TYPE_RPMMD:
+ return repomd_load_ext(data->repo, data);
+#endif
+#ifdef ENABLE_MDKREPO
+ case TYPE_MDK:
+ return mdk_load_ext(data->repo, data);
+#endif
+ default:
+ /* debian does not have any ext data yet */
+ return 0;
+ }
+}
+
+void
+read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
+{
+ Repo *repo;
+ int i;
+ Pool *sigpool = 0;
+
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ if (!cinfo->enabled)
+ continue;
+
+ repo = repo_create(pool, cinfo->alias);
+ cinfo->repo = repo;
+ repo->appdata = cinfo;
+ repo->priority = 99 - cinfo->priority;
+
+ if ((!cinfo->autorefresh || cinfo->metadata_expire) && usecachedrepo(cinfo, 0, 0))
+ {
+ printf("repo '%s':", cinfo->alias);
+ printf(" cached\n");
+ continue;
+ }
+
+ switch (cinfo->type)
+ {
+#ifdef ENABLE_RPMMD
+ case TYPE_RPMMD:
+ repomd_load(cinfo, &sigpool);
+ break;
+#endif
+#ifdef ENABLE_SUSEREPO
+ case TYPE_SUSETAGS:
+ susetags_load(cinfo, &sigpool);
+ break;
+#endif
+#ifdef ENABLE_DEBIAN
+ case TYPE_DEBIAN:
+ debian_load(cinfo, &sigpool);
+ break;
+#endif
+#ifdef ENABLE_MDKREPO
+ case TYPE_MDK:
+ mdk_load(cinfo, &sigpool);
+ break;
+#endif
+ default:
+ printf("unsupported repo '%s': skipped\n", cinfo->alias);
+ repo_free(repo, 1);
+ cinfo->repo = 0;
+ break;
+ }
+ }
+ if (sigpool)
+ pool_free(sigpool);
+}
+
diff --git a/examples/solv/repoinfo.h b/examples/solv/repoinfo.h
new file mode 100644
index 0000000..04f94b7
--- /dev/null
+++ b/examples/solv/repoinfo.h
@@ -0,0 +1,53 @@
+struct repoinfo {
+ Repo *repo;
+
+ int type;
+ char *alias;
+ char *name;
+ int enabled;
+ int autorefresh;
+ char *baseurl;
+ char *metalink;
+ char *mirrorlist;
+ char *path;
+ int pkgs_gpgcheck;
+ int repo_gpgcheck;
+ int priority;
+ int keeppackages;
+ int metadata_expire;
+ char **components;
+ int ncomponents;
+ int cookieset;
+ unsigned char cookie[32];
+ int extcookieset;
+ unsigned char extcookie[32];
+ int incomplete;
+};
+
+#define TYPE_UNKNOWN 0
+#define TYPE_SUSETAGS 1
+#define TYPE_RPMMD 2
+#define TYPE_PLAINDIR 3
+#define TYPE_DEBIAN 4
+#define TYPE_MDK 5
+
+#define TYPE_INSTALLED 16
+#define TYPE_CMDLINE 17
+
+#define METADATA_EXPIRE (60 * 15)
+
+extern void sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
+extern void free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
+extern void read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos);
+extern struct repoinfo *read_repoinfos(Pool *pool, int *nrepoinfosp);
+
+extern int read_installed_repo(struct repoinfo *cinfo, Pool *pool);
+
+extern int is_cmdline_package(const char *filename);
+extern Id add_cmdline_package(Repo *repo, const char *filename);
+
+extern void commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp);
+
+extern void add_ext_keys(Repodata *data, Id handle, const char *ext);
+extern int load_stub(Pool *pool, Repodata *data, void *dp);
+
diff --git a/examples/solv/repoinfo_cache.c b/examples/solv/repoinfo_cache.c
new file mode 100644
index 0000000..ec4fe57
--- /dev/null
+++ b/examples/solv/repoinfo_cache.c
@@ -0,0 +1,290 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_solv.h"
+#include "repo_write.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#define COOKIE_IDENT "1.1"
+
+#define SOLVCACHE_PATH "/var/cache/solv"
+
+static char *userhome;
+
+void
+set_userhome()
+{
+ userhome = getenv("HOME");
+ if (userhome && userhome[0] != '/')
+ userhome = 0;
+}
+
+void
+calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out)
+{
+ char buf[4096];
+ Chksum *h = solv_chksum_create(chktype);
+ int l;
+
+ solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
+ while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
+ solv_chksum_add(h, buf, l);
+ rewind(fp);
+ solv_chksum_free(h, out);
+}
+
+void
+calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
+{
+ Chksum *h = solv_chksum_create(chktype);
+ solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
+ if (cookie)
+ solv_chksum_add(h, cookie, 32);
+ solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
+ solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
+ solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
+ solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
+ solv_chksum_free(h, out);
+}
+
+char *
+calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc)
+{
+ char *q, *p;
+ int l;
+ if (!forcesystemloc && userhome && getuid())
+ p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
+ else
+ p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
+ l = strlen(p);
+ p = pool_tmpappend(repo->pool, p, repo->name, 0);
+ if (repoext)
+ {
+ p = pool_tmpappend(repo->pool, p, "_", repoext);
+ p = pool_tmpappend(repo->pool, p, ".solvx", 0);
+ }
+ else
+ p = pool_tmpappend(repo->pool, p, ".solv", 0);
+ q = p + l;
+ if (*q == '.')
+ *q = '_';
+ for (; *q; q++)
+ if (*q == '/')
+ *q = '_';
+ return p;
+}
+
+int
+usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark)
+{
+ Repo *repo = cinfo->repo;
+ FILE *fp;
+ unsigned char *cookie = repoext ? cinfo->extcookie : (cinfo->cookieset ? cinfo->cookie : 0);
+ unsigned char mycookie[32];
+ unsigned char myextcookie[32];
+ int flags;
+ int forcesystemloc;
+
+ if (repoext && !cinfo->extcookieset)
+ return 0; /* huh? */
+ forcesystemloc = mark & 2 ? 0 : 1;
+ if (mark < 2 && userhome && getuid())
+ {
+ /* first try home location */
+ int res = usecachedrepo(cinfo, repoext, mark | 2);
+ if (res)
+ return res;
+ }
+ mark &= 1;
+ if (!(fp = fopen(calc_cachepath(repo, repoext, forcesystemloc), "r")))
+ return 0;
+ if (!repoext && !cinfo->cookieset && cinfo->autorefresh && cinfo->metadata_expire != -1)
+ {
+ struct stat stb; /* no cookie set yet, check cache expiry time */
+ if (fstat(fileno(fp), &stb) || time(0) - stb.st_mtime >= cinfo->metadata_expire)
+ {
+ fclose(fp);
+ return 0;
+ }
+ }
+ if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
+ if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
+ {
+ fclose(fp);
+ return 0;
+ }
+ if (cinfo->type != TYPE_INSTALLED && !repoext)
+ {
+ if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
+ {
+ fclose(fp);
+ return 0;
+ }
+ }
+ rewind(fp);
+
+ flags = 0;
+ if (repoext)
+ {
+ flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+ if (strcmp(repoext, "DL") != 0)
+ flags |= REPO_LOCALPOOL; /* no local pool for DL so that we can compare IDs */
+ }
+ if (repo_add_solv(repo, fp, flags))
+ {
+ fclose(fp);
+ return 0;
+ }
+ if (cinfo->type != TYPE_INSTALLED && !repoext)
+ {
+ memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
+ cinfo->cookieset = 1;
+ memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
+ cinfo->extcookieset = 1;
+ }
+ if (mark)
+ futimens(fileno(fp), 0); /* try to set modification time */
+ fclose(fp);
+ return 1;
+}
+
+static void
+switchtowritten(struct repoinfo *cinfo, const char *repoext, Repodata *repodata, char *tmpl)
+{
+ Repo *repo = cinfo->repo;
+ FILE *fp;
+ int i;
+
+ if (!repoext && repodata)
+ return; /* rewrite case, don't bother for the added fileprovides */
+ for (i = repo->start; i < repo->end; i++)
+ if (repo->pool->solvables[i].repo != repo)
+ break;
+ if (i < repo->end)
+ return; /* not a simple block */
+ /* switch to just saved repo to activate paging and save memory */
+ fp = fopen(tmpl, "r");
+ if (!fp)
+ return;
+ if (!repoext)
+ {
+ /* main repo */
+ repo_empty(repo, 1);
+ if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
+ {
+ /* oops, no way to recover from here */
+ fprintf(stderr, "internal error\n");
+ exit(1);
+ }
+ }
+ else
+ {
+ int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+ /* make sure repodata contains complete repo */
+ /* (this is how repodata_write saves it) */
+ repodata_extend_block(repodata, repo->start, repo->end - repo->start);
+ repodata->state = REPODATA_LOADING;
+ if (strcmp(repoext, "DL") != 0)
+ flags |= REPO_LOCALPOOL;
+ repo_add_solv(repo, fp, flags);
+ repodata->state = REPODATA_AVAILABLE; /* in case the load failed */
+ }
+ fclose(fp);
+}
+
+void
+writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata)
+{
+ Repo *repo = cinfo->repo;
+ FILE *fp;
+ int fd;
+ char *tmpl, *cachedir;
+
+ if (cinfo->incomplete || (repoext && !cinfo->extcookieset) || (!repoext && !cinfo->cookieset))
+ return;
+ cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
+ if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
+ printf("[created %s]\n", cachedir);
+ /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
+ tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
+ fd = mkstemp(tmpl);
+ if (fd < 0)
+ {
+ free(tmpl);
+ return;
+ }
+ fchmod(fd, 0444);
+ if (!(fp = fdopen(fd, "w")))
+ {
+ close(fd);
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+
+ if (!repodata)
+ repo_write(repo, fp);
+ else if (repoext)
+ repodata_write(repodata, fp);
+ else
+ {
+ int oldnrepodata = repo->nrepodata;
+ repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata; /* XXX: do this right */
+ repo_write(repo, fp);
+ repo->nrepodata = oldnrepodata;
+ }
+
+ if (!repoext && cinfo->type != TYPE_INSTALLED)
+ {
+ if (!cinfo->extcookieset)
+ {
+ /* create the ext cookie and append it */
+ /* we just need some unique ID */
+ struct stat stb;
+ if (fstat(fileno(fp), &stb))
+ memset(&stb, 0, sizeof(stb));
+ calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->cookie, cinfo->extcookie);
+ cinfo->extcookieset = 1;
+ }
+ if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
+ {
+ fclose(fp);
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+ }
+ /* append our cookie describing the metadata state */
+ if (fwrite(repoext ? cinfo->extcookie : cinfo->cookie, 32, 1, fp) != 1)
+ {
+ fclose(fp);
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+ if (fclose(fp))
+ {
+ unlink(tmpl);
+ free(tmpl);
+ return;
+ }
+
+ switchtowritten(cinfo, repoext, repodata, tmpl);
+
+ if (!rename(tmpl, calc_cachepath(repo, repoext, 0)))
+ unlink(tmpl);
+ free(tmpl);
+}
+
diff --git a/examples/solv/repoinfo_cache.h b/examples/solv/repoinfo_cache.h
new file mode 100644
index 0000000..cc74715
--- /dev/null
+++ b/examples/solv/repoinfo_cache.h
@@ -0,0 +1,12 @@
+
+struct repoinfo;
+struct stat;
+
+extern void set_userhome(void);
+extern char *calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc);
+extern void calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out);
+extern void calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out);
+
+extern int usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark);
+extern void writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata);
+
diff --git a/examples/solv/repoinfo_config_debian.c b/examples/solv/repoinfo_config_debian.c
new file mode 100644
index 0000000..b369970
--- /dev/null
+++ b/examples/solv/repoinfo_config_debian.c
@@ -0,0 +1,127 @@
+#ifdef DEBIAN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_debian.h"
+
+
+
+struct repoinfo *
+read_repoinfos_debian(Pool *pool, int *nrepoinfosp)
+{
+ FILE *fp;
+ char buf[4096];
+ char buf2[4096];
+ int l;
+ char *kp, *url, *distro;
+ struct repoinfo *repoinfos = 0, *cinfo;
+ int nrepoinfos = 0;
+ DIR *dir = 0;
+ struct dirent *ent;
+
+ fp = fopen("/etc/apt/sources.list", "r");
+ while (1)
+ {
+ if (!fp)
+ {
+ if (!dir)
+ {
+ dir = opendir("/etc/apt/sources.list.d");
+ if (!dir)
+ break;
+ }
+ if ((ent = readdir(dir)) == 0)
+ {
+ closedir(dir);
+ break;
+ }
+ if (ent->d_name[0] == '.')
+ continue;
+ l = strlen(ent->d_name);
+ if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
+ continue;
+ snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
+ if (!(fp = fopen(buf, "r")))
+ continue;
+ }
+ while(fgets(buf2, sizeof(buf2), fp))
+ {
+ l = strlen(buf2);
+ if (l == 0)
+ continue;
+ while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
+ buf2[--l] = 0;
+ kp = buf2;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp || *kp == '#')
+ continue;
+ if (strncmp(kp, "deb", 3) != 0)
+ continue;
+ kp += 3;
+ if (*kp != ' ' && *kp != '\t')
+ continue;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ continue;
+ url = kp;
+ while (*kp && *kp != ' ' && *kp != '\t')
+ kp++;
+ if (*kp)
+ *kp++ = 0;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ continue;
+ distro = kp;
+ while (*kp && *kp != ' ' && *kp != '\t')
+ kp++;
+ if (*kp)
+ *kp++ = 0;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ continue;
+ repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+ cinfo = repoinfos + nrepoinfos++;
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->baseurl = strdup(url);
+ cinfo->alias = solv_dupjoin(url, "/", distro);
+ cinfo->name = strdup(distro);
+ cinfo->type = TYPE_DEBIAN;
+ cinfo->enabled = 1;
+ cinfo->autorefresh = 1;
+ cinfo->repo_gpgcheck = 1;
+ cinfo->metadata_expire = METADATA_EXPIRE;
+ while (*kp)
+ {
+ char *compo;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp)
+ break;
+ compo = kp;
+ while (*kp && *kp != ' ' && *kp != '\t')
+ kp++;
+ if (*kp)
+ *kp++ = 0;
+ cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
+ cinfo->components[cinfo->ncomponents++] = strdup(compo);
+ }
+ }
+ fclose(fp);
+ fp = 0;
+ }
+ *nrepoinfosp = nrepoinfos;
+ return repoinfos;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_config_debian.h b/examples/solv/repoinfo_config_debian.h
new file mode 100644
index 0000000..64b3eb6
--- /dev/null
+++ b/examples/solv/repoinfo_config_debian.h
@@ -0,0 +1 @@
+extern struct repoinfo *read_repoinfos_debian(Pool *pool, int *nrepoinfosp);
diff --git a/examples/solv/repoinfo_config_urpmi.c b/examples/solv/repoinfo_config_urpmi.c
new file mode 100644
index 0000000..a79ea9b
--- /dev/null
+++ b/examples/solv/repoinfo_config_urpmi.c
@@ -0,0 +1,106 @@
+#if defined(MANDRIVA) || defined(MAGEIA)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_urpmi.h"
+
+
+#define URPMI_CFG "/etc/urpmi/urpmi.cfg"
+
+
+struct repoinfo *
+read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp)
+{
+ char buf[4096], *bp, *arg;
+ FILE *fp;
+ int l, insect = 0;
+ struct repoinfo *cinfo = 0;
+ struct repoinfo *repoinfos = 0;
+ int nrepoinfos = 0;
+
+ if ((fp = fopen(URPMI_CFG, "r")) == 0)
+ {
+ *nrepoinfosp = 0;
+ return 0;
+ }
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ l = strlen(buf);
+ while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+ buf[--l] = 0;
+ bp = buf;
+ while (l && (*bp == ' ' || *bp == '\t'))
+ {
+ l--;
+ bp++;
+ }
+ if (!l || *bp == '#')
+ continue;
+ if (!insect && bp[l - 1] == '{')
+ {
+ insect++;
+ bp[--l] = 0;
+ if (l > 0)
+ {
+ while (l && (bp[l - 1] == ' ' || bp[l - 1] == '\t'))
+ bp[--l] = 0;
+ }
+ if (l)
+ {
+ char *bbp = bp, *bbp2 = bp;
+ /* unescape */
+ while (*bbp)
+ {
+ if (*bbp == '\\' && bbp[1])
+ bbp++;
+ *bbp2++ = *bbp++;
+ }
+ *bbp2 = 0;
+ repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+ cinfo = repoinfos + nrepoinfos++;
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->alias = strdup(bp);
+ cinfo->type = TYPE_MDK;
+ cinfo->autorefresh = 1;
+ cinfo->priority = 99;
+ cinfo->enabled = 1;
+ cinfo->metadata_expire = METADATA_EXPIRE;
+ }
+ continue;
+ }
+ if (insect && *bp == '}')
+ {
+ insect--;
+ cinfo = 0;
+ continue;
+ }
+ if (!insect || !cinfo)
+ continue;
+ if ((arg = strchr(bp, ':')) != 0)
+ {
+ *arg++ = 0;
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+ if (!*arg)
+ arg = 0;
+ }
+ if (strcmp(bp, "ignore") == 0)
+ cinfo->enabled = 0;
+ if (strcmp(bp, "mirrorlist") == 0)
+ cinfo->mirrorlist = solv_strdup(arg);
+ if (strcmp(bp, "with-dir") == 0)
+ cinfo->path = solv_strdup(arg);
+ }
+ fclose(fp);
+ *nrepoinfosp = nrepoinfos;
+ return repoinfos;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_config_urpmi.h b/examples/solv/repoinfo_config_urpmi.h
new file mode 100644
index 0000000..bb7374f
--- /dev/null
+++ b/examples/solv/repoinfo_config_urpmi.h
@@ -0,0 +1,2 @@
+extern struct repoinfo *read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp);
+
diff --git a/examples/solv/repoinfo_config_yum.c b/examples/solv/repoinfo_config_yum.c
new file mode 100644
index 0000000..6e2e66a
--- /dev/null
+++ b/examples/solv/repoinfo_config_yum.c
@@ -0,0 +1,233 @@
+#if defined(SUSE) || defined(FEDORA)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_yum.h"
+
+
+#ifdef FEDORA
+# define REPOINFO_PATH "/etc/yum.repos.d"
+#endif
+#ifdef SUSE
+# define REPOINFO_PATH "/etc/zypp/repos.d"
+#endif
+
+char *
+yum_substitute(Pool *pool, char *line)
+{
+ char *p, *p2;
+ static char *releaseevr;
+ static char *basearch;
+
+ if (!line)
+ {
+ solv_free(releaseevr);
+ releaseevr = 0;
+ solv_free(basearch);
+ basearch = 0;
+ return 0;
+ }
+ p = line;
+ while ((p2 = strchr(p, '$')) != 0)
+ {
+ if (!strncmp(p2, "$releasever", 11))
+ {
+ if (!releaseevr)
+ {
+ void *rpmstate;
+ Queue q;
+
+ queue_init(&q);
+ rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+ rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
+ if (q.count)
+ {
+ void *handle;
+ char *p;
+ handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
+ releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
+ if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
+ *p = 0;
+ }
+ rpm_state_free(rpmstate);
+ queue_free(&q);
+ if (!releaseevr)
+ {
+ fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
+ exit(1);
+ }
+ }
+ *p2 = 0;
+ p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
+ p2 = p + (p2 - line);
+ line = p;
+ p = p2 + strlen(releaseevr);
+ continue;
+ }
+ if (!strncmp(p2, "$basearch", 9))
+ {
+ if (!basearch)
+ {
+ struct utsname un;
+ if (uname(&un))
+ {
+ perror("uname");
+ exit(1);
+ }
+ basearch = strdup(un.machine);
+ if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
+ basearch[1] = '3';
+ }
+ *p2 = 0;
+ p = pool_tmpjoin(pool, line, basearch, p2 + 9);
+ p2 = p + (p2 - line);
+ line = p;
+ p = p2 + strlen(basearch);
+ continue;
+ }
+ p = p2 + 1;
+ }
+ return line;
+}
+
+struct repoinfo *
+read_repoinfos_yum(Pool *pool, int *nrepoinfosp)
+{
+ const char *reposdir = REPOINFO_PATH;
+ char buf[4096];
+ char buf2[4096], *kp, *vp, *kpe;
+ DIR *dir;
+ FILE *fp;
+ struct dirent *ent;
+ int l, rdlen;
+ struct repoinfo *repoinfos = 0, *cinfo;
+ int nrepoinfos = 0;
+
+ rdlen = strlen(reposdir);
+ dir = opendir(reposdir);
+ if (!dir)
+ {
+ *nrepoinfosp = 0;
+ return 0;
+ }
+ while ((ent = readdir(dir)) != 0)
+ {
+ if (ent->d_name[0] == '.')
+ continue;
+ l = strlen(ent->d_name);
+ if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
+ continue;
+ snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
+ if ((fp = fopen(buf, "r")) == 0)
+ {
+ perror(buf);
+ continue;
+ }
+ cinfo = 0;
+ while(fgets(buf2, sizeof(buf2), fp))
+ {
+ l = strlen(buf2);
+ if (l == 0)
+ continue;
+ while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
+ buf2[--l] = 0;
+ kp = buf2;
+ while (*kp == ' ' || *kp == '\t')
+ kp++;
+ if (!*kp || *kp == '#')
+ continue;
+ if (strchr(kp, '$'))
+ kp = yum_substitute(pool, kp);
+ if (*kp == '[')
+ {
+ vp = strrchr(kp, ']');
+ if (!vp)
+ continue;
+ *vp = 0;
+ repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+ cinfo = repoinfos + nrepoinfos++;
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->alias = strdup(kp + 1);
+ cinfo->type = TYPE_RPMMD;
+ cinfo->autorefresh = 1;
+ cinfo->priority = 99;
+#ifndef FEDORA
+ cinfo->repo_gpgcheck = 1;
+#endif
+ cinfo->metadata_expire = METADATA_EXPIRE;
+ continue;
+ }
+ if (!cinfo)
+ continue;
+ vp = strchr(kp, '=');
+ if (!vp)
+ continue;
+ for (kpe = vp - 1; kpe >= kp; kpe--)
+ if (*kpe != ' ' && *kpe != '\t')
+ break;
+ if (kpe == kp)
+ continue;
+ vp++;
+ while (*vp == ' ' || *vp == '\t')
+ vp++;
+ kpe[1] = 0;
+ if (!strcmp(kp, "name"))
+ cinfo->name = strdup(vp);
+ else if (!strcmp(kp, "enabled"))
+ cinfo->enabled = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "autorefresh"))
+ cinfo->autorefresh = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "gpgcheck"))
+ cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "repo_gpgcheck"))
+ cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
+ else if (!strcmp(kp, "baseurl"))
+ cinfo->baseurl = strdup(vp);
+ else if (!strcmp(kp, "mirrorlist"))
+ {
+ if (strstr(vp, "metalink"))
+ cinfo->metalink = strdup(vp);
+ else
+ cinfo->mirrorlist = strdup(vp);
+ }
+ else if (!strcmp(kp, "path"))
+ {
+ if (vp && strcmp(vp, "/") != 0)
+ cinfo->path = strdup(vp);
+ }
+ else if (!strcmp(kp, "type"))
+ {
+ if (!strcmp(vp, "yast2"))
+ cinfo->type = TYPE_SUSETAGS;
+ else if (!strcmp(vp, "rpm-md"))
+ cinfo->type = TYPE_RPMMD;
+ else if (!strcmp(vp, "plaindir"))
+ cinfo->type = TYPE_PLAINDIR;
+ else if (!strcmp(vp, "mdk"))
+ cinfo->type = TYPE_MDK;
+ else
+ cinfo->type = TYPE_UNKNOWN;
+ }
+ else if (!strcmp(kp, "priority"))
+ cinfo->priority = atoi(vp);
+ else if (!strcmp(kp, "keeppackages"))
+ cinfo->keeppackages = *vp == '0' ? 0 : 1;
+ }
+ fclose(fp);
+ cinfo = 0;
+ }
+ closedir(dir);
+ *nrepoinfosp = nrepoinfos;
+ return repoinfos;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_config_yum.h b/examples/solv/repoinfo_config_yum.h
new file mode 100644
index 0000000..d7cb4f7
--- /dev/null
+++ b/examples/solv/repoinfo_config_yum.h
@@ -0,0 +1,2 @@
+extern char *yum_substitute(Pool *pool, char *line);
+extern struct repoinfo *read_repoinfos_yum(Pool *pool, int *nrepoinfosp);
diff --git a/examples/solv/repoinfo_download.c b/examples/solv/repoinfo_download.c
new file mode 100644
index 0000000..5ba3014
--- /dev/null
+++ b/examples/solv/repoinfo_download.c
@@ -0,0 +1,221 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "solv_xfopen.h"
+
+#include "repoinfo.h"
+#include "mirror.h"
+#include "checksig.h"
+#include "repoinfo_download.h"
+
+static inline int
+opentmpfile()
+{
+ char tmpl[100];
+ int fd;
+
+ strcpy(tmpl, "/var/tmp/solvXXXXXX");
+ fd = mkstemp(tmpl);
+ if (fd < 0)
+ {
+ perror("mkstemp");
+ exit(1);
+ }
+ unlink(tmpl);
+ return fd;
+}
+
+int
+verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
+{
+ char buf[1024];
+ const unsigned char *sum;
+ Chksum *h;
+ int l;
+
+ h = solv_chksum_create(chksumtype);
+ if (!h)
+ {
+ printf("%s: unknown checksum type\n", file);
+ return 0;
+ }
+ while ((l = read(fd, buf, sizeof(buf))) > 0)
+ solv_chksum_add(h, buf, l);
+ lseek(fd, 0, SEEK_SET);
+ l = 0;
+ sum = solv_chksum_get(h, &l);
+ if (memcmp(sum, chksum, l))
+ {
+ printf("%s: checksum mismatch\n", file);
+ solv_chksum_free(h, 0);
+ return 0;
+ }
+ solv_chksum_free(h, 0);
+ return 1;
+}
+
+FILE *
+curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
+{
+ FILE *fp;
+ pid_t pid;
+ int fd;
+ int status;
+ char url[4096];
+ const char *baseurl = cinfo->baseurl;
+
+ if (!baseurl)
+ {
+ if (!cinfo->metalink && !cinfo->mirrorlist)
+ return 0;
+ if (file != cinfo->metalink && file != cinfo->mirrorlist)
+ {
+ unsigned char mlchksum[32];
+ Id mlchksumtype = 0;
+ fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
+ if (!fp)
+ return 0;
+ if (cinfo->metalink)
+ cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
+ else
+ cinfo->baseurl = findmirrorlisturl(fp);
+ fclose(fp);
+ if (!cinfo->baseurl)
+ return 0;
+#ifdef FEDORA
+ if (strchr(cinfo->baseurl, '$'))
+ {
+ char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
+ free(cinfo->baseurl);
+ cinfo->baseurl = strdup(b);
+ }
+#endif
+ if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
+ {
+ chksumtype = mlchksumtype;
+ chksum = mlchksum;
+ }
+ return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
+ }
+ snprintf(url, sizeof(url), "%s", file);
+ }
+ else
+ {
+ const char *path = cinfo->path && strcmp(cinfo->path, "/") != 0 ? cinfo->path : "";
+ int l = strlen(baseurl);
+ int pl = strlen(path);
+ const char *sep = l && baseurl[l - 1] == '/' ? "" : "/";
+ const char *psep = pl && cinfo->path[pl - 1] == '/' ? "" : "/";
+ snprintf(url, sizeof(url), "%s%s%s%s%s", baseurl, sep, path, psep, file);
+ }
+ fd = opentmpfile();
+ // printf("url: %s\n", url);
+ if ((pid = fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0)
+ {
+ if (fd != 1)
+ {
+ dup2(fd, 1);
+ close(fd);
+ }
+ execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
+ perror("curl");
+ _exit(0);
+ }
+ status = 0;
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
+ {
+ /* empty file */
+ close(fd);
+ return 0;
+ }
+ lseek(fd, 0, SEEK_SET);
+ if (status)
+ {
+ printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
+ if (markincomplete)
+ cinfo->incomplete = 1;
+ close(fd);
+ return 0;
+ }
+ if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
+ {
+ if (markincomplete)
+ cinfo->incomplete = 1;
+ close(fd);
+ return 0;
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (uncompress)
+ {
+ if (solv_xfopen_iscompressed(file) < 0)
+ {
+ printf("%s: unsupported compression\n", file);
+ if (markincomplete)
+ cinfo->incomplete = 1;
+ close(fd);
+ return 0;
+ }
+ fp = solv_xfopen_fd(file, fd, "r");
+ }
+ else
+ fp = fdopen(fd, "r");
+ if (!fp)
+ close(fd);
+ return fp;
+}
+
+FILE *
+downloadpackage(Solvable *s, const char *loc)
+{
+ const unsigned char *chksum;
+ Id chksumtype;
+ struct repoinfo *cinfo = s->repo->appdata;
+
+#ifdef ENABLE_SUSEREPO
+ if (cinfo->type == TYPE_SUSETAGS)
+ {
+ const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
+ loc = pool_tmpjoin(s->repo->pool, datadir ? datadir : "suse", "/", loc);
+ }
+#endif
+ chksumtype = 0;
+ chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
+ return curlfopen(cinfo, loc, 0, chksum, chksumtype, 0);
+}
+
+int
+downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
+{
+ FILE *sigfp;
+ sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0);
+ if (!sigfp)
+ {
+ printf(" unsigned, skipped\n");
+ return 0;
+ }
+ if (!*sigpool)
+ *sigpool = read_sigs();
+ if (!checksig(*sigpool, fp, sigfp))
+ {
+ printf(" checksig failed, skipped\n");
+ fclose(sigfp);
+ return 0;
+ }
+ fclose(sigfp);
+ return 1;
+}
+
diff --git a/examples/solv/repoinfo_download.h b/examples/solv/repoinfo_download.h
new file mode 100644
index 0000000..2dfeaa0
--- /dev/null
+++ b/examples/solv/repoinfo_download.h
@@ -0,0 +1,7 @@
+int verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype);
+
+FILE *curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete);
+
+FILE *downloadpackage(Solvable *s, const char *loc);
+int downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool);
+
diff --git a/examples/solv/repoinfo_system_debian.c b/examples/solv/repoinfo_system_debian.c
new file mode 100644
index 0000000..f01be60
--- /dev/null
+++ b/examples/solv/repoinfo_system_debian.c
@@ -0,0 +1,108 @@
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_deb.h"
+#include "transaction.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_system_debian.h"
+
+static void
+rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
+{
+ pid_t pid;
+ int status;
+
+ if ((pid = fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0)
+ {
+ if (!rootdir)
+ rootdir = "/";
+ if (dupfd3 != -1 && dupfd3 != 3)
+ {
+ dup2(dupfd3, 3);
+ close(dupfd3);
+ }
+ if (dupfd3 != -1)
+ fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
+ if (strcmp(arg, "--install") == 0)
+ execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
+ else
+ execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
+ perror("dpkg");
+ _exit(0);
+ }
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ if (status)
+ {
+ printf("dpkg failed\n");
+ exit(1);
+ }
+}
+
+int
+read_installed_debian(struct repoinfo *cinfo)
+{
+ struct stat stb;
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+
+ memset(&stb, 0, sizeof(stb));
+ printf("dpgk database:");
+ if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
+ memset(&stb, 0, sizeof(stb));
+ calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 0))
+ {
+ printf(" cached\n");
+ return 1;
+ }
+ if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
+ return 0;
+ }
+ repo_internalize(repo);
+ writecachedrepo(cinfo, 0, 0);
+ return 1;
+}
+
+void
+commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp)
+{
+ Solvable *s = pool_id2solvable(pool, p);
+ const char *rootdir = pool_get_rootdir(pool);
+
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ rewind(fp);
+ lseek(fileno(fp), 0, SEEK_SET);
+ rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
diff --git a/examples/solv/repoinfo_system_debian.h b/examples/solv/repoinfo_system_debian.h
new file mode 100644
index 0000000..c0e2b29
--- /dev/null
+++ b/examples/solv/repoinfo_system_debian.h
@@ -0,0 +1,2 @@
+int read_installed_debian(struct repoinfo *cinfo);
+void commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp);
diff --git a/examples/solv/repoinfo_system_rpm.c b/examples/solv/repoinfo_system_rpm.c
new file mode 100644
index 0000000..b385d72
--- /dev/null
+++ b/examples/solv/repoinfo_system_rpm.c
@@ -0,0 +1,151 @@
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#if defined(ENABLE_SUSEREPO) && defined(SUSE)
+#include "repo_products.h"
+#endif
+#if defined(ENABLE_APPDATA)
+#include "repo_appdata.h"
+#endif
+#include "transaction.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_system_rpm.h"
+
+#ifdef SUSE
+# define PRODUCTS_PATH "/etc/products.d"
+#endif
+#ifdef ENABLE_APPDATA
+# define APPDATA_PATH "/usr/share/appdata"
+#endif
+
+static void
+runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
+{
+ pid_t pid;
+ int status;
+
+ if ((pid = fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0)
+ {
+ if (!rootdir)
+ rootdir = "/";
+ if (dupfd3 != -1 && dupfd3 != 3)
+ {
+ dup2(dupfd3, 3);
+ close(dupfd3);
+ }
+ if (dupfd3 != -1)
+ fcntl(3, F_SETFD, 0); /* clear CLOEXEC */
+ if (strcmp(arg, "-e") == 0)
+ execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
+ else
+ execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
+ perror("rpm");
+ _exit(0);
+ }
+ while (waitpid(pid, &status, 0) != pid)
+ ;
+ if (status)
+ {
+ printf("rpm failed\n");
+ exit(1);
+ }
+}
+
+int
+read_installed_rpm(struct repoinfo *cinfo)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ FILE *ofp = 0;
+ struct stat stb;
+
+ memset(&stb, 0, sizeof(stb));
+ printf("rpm database:");
+ if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
+ memset(&stb, 0, sizeof(stb));
+ calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 0))
+ {
+ printf(" cached\n");
+ return 1;
+ }
+ printf(" reading\n");
+#if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
+ if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
+ return 0;
+ }
+#endif
+#if defined(ENABLE_APPDATA) && defined(APPDATA_PATH)
+ if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
+ return 0;
+ }
+#endif
+ ofp = fopen(calc_cachepath(repo, 0, 0), "r");
+ if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+ {
+ fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
+ return 0;
+ }
+ if (ofp)
+ fclose(ofp);
+ repo_internalize(repo);
+ writecachedrepo(cinfo, 0, 0);
+ return 1;
+}
+
+void
+commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp)
+{
+ Solvable *s = pool_id2solvable(pool, p);
+ const char *rootdir = pool_get_rootdir(pool);
+ const char *evr, *evrp, *nvra;
+
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
+ break;
+ /* strip epoch from evr */
+ evr = evrp = pool_id2str(pool, s->evr);
+ while (*evrp >= '0' && *evrp <= '9')
+ evrp++;
+ if (evrp > evr && evrp[0] == ':' && evrp[1])
+ evr = evrp + 1;
+ nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
+ nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
+ runrpm("-e", nvra, -1, rootdir); /* too bad that --querybynumber doesn't work */
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ rewind(fp);
+ lseek(fileno(fp), 0, SEEK_SET);
+ runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
diff --git a/examples/solv/repoinfo_system_rpm.h b/examples/solv/repoinfo_system_rpm.h
new file mode 100644
index 0000000..db63a8d
--- /dev/null
+++ b/examples/solv/repoinfo_system_rpm.h
@@ -0,0 +1,2 @@
+int read_installed_rpm(struct repoinfo *cinfo);
+void commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp);
diff --git a/examples/solv/repoinfo_type_debian.c b/examples/solv/repoinfo_type_debian.c
new file mode 100644
index 0000000..7a15ff4
--- /dev/null
+++ b/examples/solv/repoinfo_type_debian.c
@@ -0,0 +1,202 @@
+#ifdef ENABLE_DEBIAN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_deb.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_debian.h"
+
+static const char *
+debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
+{
+ char buf[4096];
+ Id chksumtype;
+ unsigned char *chksum;
+ Id curchksumtype;
+ int l, compl;
+ char *ch, *fn, *bp;
+ char *filename;
+ static char *basearch;
+ char *binarydir;
+ int lbinarydir;
+
+ if (!basearch)
+ {
+ struct utsname un;
+ if (uname(&un))
+ {
+ perror("uname");
+ exit(1);
+ }
+ basearch = strdup(un.machine);
+ if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
+ basearch[1] = '3';
+ }
+ binarydir = solv_dupjoin("binary-", basearch, "/");
+ lbinarydir = strlen(binarydir);
+ compl = strlen(comp);
+ rewind(fp);
+ curchksumtype = 0;
+ filename = 0;
+ chksum = solv_malloc(32);
+ chksumtype = 0;
+ while(fgets(buf, sizeof(buf), fp))
+ {
+ l = strlen(buf);
+ if (l == 0)
+ continue;
+ while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+ buf[--l] = 0;
+ if (!strncasecmp(buf, "MD5Sum:", 7))
+ {
+ curchksumtype = REPOKEY_TYPE_MD5;
+ continue;
+ }
+ if (!strncasecmp(buf, "SHA1:", 5))
+ {
+ curchksumtype = REPOKEY_TYPE_SHA1;
+ continue;
+ }
+ if (!strncasecmp(buf, "SHA256:", 7))
+ {
+ curchksumtype = REPOKEY_TYPE_SHA256;
+ continue;
+ }
+ if (!curchksumtype)
+ continue;
+ bp = buf;
+ if (*bp++ != ' ')
+ {
+ curchksumtype = 0;
+ continue;
+ }
+ ch = bp;
+ while (*bp && *bp != ' ' && *bp != '\t')
+ bp++;
+ if (!*bp)
+ continue;
+ *bp++ = 0;
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ while (*bp && *bp != ' ' && *bp != '\t')
+ bp++;
+ if (!*bp)
+ continue;
+ while (*bp == ' ' || *bp == '\t')
+ bp++;
+ fn = bp;
+ if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
+ continue;
+ bp += compl + 1;
+ if (strncmp(bp, binarydir, lbinarydir))
+ continue;
+ bp += lbinarydir;
+ if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
+ {
+ unsigned char curchksum[32];
+ int curl;
+ if (filename && !strcmp(bp, "Packages"))
+ continue;
+ curl = solv_chksum_len(curchksumtype);
+ if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
+ continue;
+ if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
+ continue;
+ solv_free(filename);
+ filename = strdup(fn);
+ chksumtype = curchksumtype;
+ memcpy(chksum, curchksum, curl);
+ }
+ }
+ free(binarydir);
+ if (filename)
+ {
+ fn = solv_dupjoin("/", filename, 0);
+ solv_free(filename);
+ filename = solv_dupjoin("dists/", cinfo->name, fn);
+ solv_free(fn);
+ }
+ if (!chksumtype)
+ chksum = solv_free(chksum);
+ *chksump = chksum;
+ *chksumtypep = chksumtype;
+ return filename;
+}
+
+int
+debian_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ const char *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ FILE *fp, *fpr;
+ int j;
+
+ printf("debian repo '%s':", cinfo->alias);
+ fflush(stdout);
+ filename = solv_dupjoin("dists/", cinfo->name, "/Release");
+ if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
+ {
+ printf(" no Release file\n");
+ free((char *)filename);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ solv_free((char *)filename);
+ if (cinfo->repo_gpgcheck)
+ {
+ filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
+ if (!downloadchecksig(cinfo, fpr, filename, sigpoolp))
+ {
+ fclose(fpr);
+ solv_free((char *)filename);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ solv_free((char *)filename);
+ }
+ calc_cookie_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fpr);
+ return 1;
+ }
+ printf(" fetching\n");
+ for (j = 0; j < cinfo->ncomponents; j++)
+ {
+ if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
+ {
+ printf("[component %s not found]\n", cinfo->components[j]);
+ continue;
+ }
+ if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_debpackages(repo, fp, 0))
+ {
+ printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ solv_free((char *)filechksum);
+ solv_free((char *)filename);
+ }
+ fclose(fpr);
+ writecachedrepo(cinfo, 0, 0);
+ return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_debian.h b/examples/solv/repoinfo_type_debian.h
new file mode 100644
index 0000000..3dfc827
--- /dev/null
+++ b/examples/solv/repoinfo_type_debian.h
@@ -0,0 +1,2 @@
+extern int debian_load(struct repoinfo *cinfo, Pool **sigpoolp);
+
diff --git a/examples/solv/repoinfo_type_mdk.c b/examples/solv/repoinfo_type_mdk.c
new file mode 100644
index 0000000..96b005f
--- /dev/null
+++ b/examples/solv/repoinfo_type_mdk.c
@@ -0,0 +1,213 @@
+#ifdef ENABLE_MDKREPO
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_mdk.h"
+#include "solv_xfopen.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_mdk.h"
+
+static int
+mdk_find(const char *md5sums, const char *what, unsigned char *chksum)
+{
+ const char *sp, *ep;
+ int wl = strlen(what);
+ for (sp = md5sums; (ep = strchr(sp, '\n')) != 0; sp = ep + 1)
+ {
+ int l = ep - sp;
+ if (l <= 34)
+ continue;
+ if (sp[32] != ' ' || sp[33] != ' ')
+ continue;
+ if (wl != l - 34 || strncmp(what, sp + 34, wl) != 0)
+ continue;
+ if (solv_hex2bin(&sp, chksum, 16) != 16)
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+slurp(FILE *fp)
+{
+ int l, ll;
+ char *buf = 0;
+ int bufl = 0;
+
+ for (l = 0; ; l += ll)
+ {
+ if (bufl - l < 4096)
+ {
+ bufl += 4096;
+ buf = solv_realloc(buf, bufl);
+ }
+ ll = fread(buf + l, 1, bufl - l, fp);
+ if (ll < 0)
+ {
+ buf = solv_free(buf);
+ l = 0;
+ break;
+ }
+ if (ll == 0)
+ {
+ buf[l] = 0;
+ break;
+ }
+ }
+ return buf;
+}
+
+int
+mdk_load_ext(Repo *repo, Repodata *data)
+{
+ struct repoinfo *cinfo = repo->appdata;
+ const char *type, *ext, *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ int r = 0;
+ FILE *fp;
+
+ type = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
+ if (strcmp(type, "filelists") != 0)
+ return 0;
+ ext = "FL";
+ printf("[%s:%s", repo->name, ext);
+ if (usecachedrepo(cinfo, ext, 0))
+ {
+ printf(" cached]\n"); fflush(stdout);
+ return 1;
+ }
+ printf(" fetching]\n"); fflush(stdout);
+ filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
+ filechksumtype = 0;
+ filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
+ if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
+ return 0;
+ r = repo_add_mdk_info(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+ fclose(fp);
+ if (r)
+ {
+ printf("%s\n", pool_errstr(repo->pool));
+ return 0;
+ }
+ writecachedrepo(cinfo, ext, data);
+ return 1;
+}
+
+int
+mdk_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ const char *compression;
+ FILE *fp, *cfp;
+ char *md5sums;
+ unsigned char probe[5];
+ unsigned char md5[16];
+
+ printf("mdk repo '%s':", cinfo->alias);
+ fflush(stdout);
+ if ((fp = curlfopen(cinfo, "media_info/MD5SUM", 0, 0, 0, 0)) == 0)
+ {
+ printf(" no media_info/MD5SUM file\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fp);
+ return 1;
+ }
+ md5sums = slurp(fp);
+ fclose(fp);
+ printf(" fetching\n");
+ if (!mdk_find(md5sums, "synthesis.hdlist.cz", md5))
+ {
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if ((fp = curlfopen(cinfo, "media_info/synthesis.hdlist.cz", 0, md5, REPOKEY_TYPE_MD5, 1)) == 0)
+ {
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ /* probe compression */
+ if (fread(probe, 5, 1, fp) != 1)
+ {
+ fclose(fp);
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if (probe[0] == 0xfd && memcmp(probe + 1, "7zXZ", 4) == 0)
+ compression = "synthesis.hdlist.xz";
+ else
+ compression = "synthesis.hdlist.gz";
+ lseek(fileno(fp), 0, SEEK_SET);
+ cfp = solv_xfopen_fd(compression, dup(fileno(fp)), "r");
+ fclose(fp);
+ fp = cfp;
+ if (!fp)
+ {
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if (repo_add_mdk(repo, fp, REPO_NO_INTERNALIZE))
+ {
+ printf("synthesis.hdlist.cz: %s\n", pool_errstr(pool));
+ fclose(fp);
+ solv_free(md5sums);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ fclose(fp);
+ /* add info, could do this on demand, but always having the summary is nice */
+ if (mdk_find(md5sums, "info.xml.lzma", md5))
+ {
+ if ((fp = curlfopen(cinfo, "media_info/info.xml.lzma", 1, md5, REPOKEY_TYPE_MD5, 1)) != 0)
+ {
+ if (repo_add_mdk_info(repo, fp, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
+ {
+ printf("info.xml.lzma: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ }
+ repo_internalize(repo);
+ data = repo_add_repodata(repo, 0);
+ /* setup on-demand loading of filelist data */
+ if (mdk_find(md5sums, "files.xml.lzma", md5))
+ {
+ Id handle = repodata_new_handle(data);
+ /* we mis-use the repomd ids here... need something generic in the future */
+ repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, "filelists");
+ repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, "media_info/files.xml.lzma");
+ repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, REPOKEY_TYPE_MD5, md5);
+ add_ext_keys(data, handle, "FL");
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+ }
+ solv_free(md5sums);
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, 0);
+ repodata_create_stubs(repo_last_repodata(repo));
+ return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_mdk.h b/examples/solv/repoinfo_type_mdk.h
new file mode 100644
index 0000000..e98bc48
--- /dev/null
+++ b/examples/solv/repoinfo_type_mdk.h
@@ -0,0 +1,3 @@
+extern int mdk_load(struct repoinfo *cinfo, Pool **sigpoolp);
+extern int mdk_load_ext(Repo *repo, Repodata *data);
+
diff --git a/examples/solv/repoinfo_type_rpmmd.c b/examples/solv/repoinfo_type_rpmmd.c
new file mode 100644
index 0000000..98c5d36
--- /dev/null
+++ b/examples/solv/repoinfo_type_rpmmd.c
@@ -0,0 +1,207 @@
+#ifdef ENABLE_RPMMD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_rpmmd.h"
+#include "repo_deltainfoxml.h"
+#include "repo_updateinfoxml.h"
+#include "repo_repomdxml.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_rpmmd.h"
+
+
+
+static const char *
+repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
+{
+ Pool *pool = repo->pool;
+ Dataiterator di;
+ const char *filename;
+
+ filename = 0;
+ *chksump = 0;
+ *chksumtypep = 0;
+ dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
+ dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
+ if (dataiterator_step(&di))
+ {
+ dataiterator_setpos_parent(&di);
+ filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
+ *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
+ }
+ dataiterator_free(&di);
+ if (filename && !*chksumtypep)
+ {
+ printf("no %s file checksum!\n", what);
+ filename = 0;
+ }
+ return filename;
+}
+
+static void
+repomd_add_ext(Repo *repo, Repodata *data, const char *what, const char *ext)
+{
+ Id chksumtype, handle;
+ const unsigned char *chksum;
+ const char *filename;
+
+ filename = repomd_find(repo, what, &chksum, &chksumtype);
+ if (!filename && !strcmp(what, "deltainfo"))
+ filename = repomd_find(repo, "prestodelta", &chksum, &chksumtype);
+ if (!filename)
+ return;
+ handle = repodata_new_handle(data);
+ repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
+ repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
+ repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
+ add_ext_keys(data, handle, ext);
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+}
+
+int
+repomd_load_ext(Repo *repo, Repodata *data)
+{
+ const char *filename, *repomdtype;
+ char ext[3];
+ FILE *fp;
+ struct repoinfo *cinfo;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ int r = 0;
+
+ cinfo = repo->appdata;
+ repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
+ if (!repomdtype)
+ return 0;
+ if (!strcmp(repomdtype, "filelists"))
+ strcpy(ext, "FL");
+ else if (!strcmp(repomdtype, "deltainfo"))
+ strcpy(ext, "DL");
+ else
+ return 0;
+ printf("[%s:%s", repo->name, ext);
+ if (usecachedrepo(cinfo, ext, 0))
+ {
+ printf(" cached]\n"); fflush(stdout);
+ return 1;
+ }
+ printf(" fetching]\n"); fflush(stdout);
+ filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
+ filechksumtype = 0;
+ filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
+ if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
+ return 0;
+ if (!strcmp(ext, "FL"))
+ r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+ else if (!strcmp(ext, "DL"))
+ r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
+ fclose(fp);
+ if (r)
+ {
+ printf("%s\n", pool_errstr(repo->pool));
+ return 0;
+ }
+ if (cinfo->extcookieset)
+ writecachedrepo(cinfo, ext, data);
+ return 1;
+}
+
+int
+repomd_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ const char *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ FILE *fp;
+
+ printf("rpmmd repo '%s':", cinfo->alias);
+ fflush(stdout);
+ if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
+ {
+ printf(" no repomd.xml file\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fp);
+ return 1;
+ }
+ if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", sigpoolp))
+ {
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ if (repo_add_repomdxml(repo, fp, 0))
+ {
+ printf("repomd.xml: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ printf(" fetching\n");
+ filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_rpmmd(repo, fp, 0, 0))
+ {
+ printf("primary: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ if (cinfo->incomplete)
+ return 0; /* hopeless */
+
+ filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_updateinfoxml(repo, fp, 0))
+ {
+ printf("updateinfo: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+
+#ifdef ENABLE_APPDATA
+ filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_appdata(repo, fp, 0))
+ {
+ printf("appdata: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+#endif
+ data = repo_add_repodata(repo, 0);
+ repomd_add_ext(repo, data, "deltainfo", "DL");
+ repomd_add_ext(repo, data, "filelists", "FL");
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, 0);
+ repodata_create_stubs(repo_last_repodata(repo));
+ return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_rpmmd.h b/examples/solv/repoinfo_type_rpmmd.h
new file mode 100644
index 0000000..275dfc1
--- /dev/null
+++ b/examples/solv/repoinfo_type_rpmmd.h
@@ -0,0 +1,2 @@
+extern int repomd_load_ext(Repo *repo, Repodata *data);
+extern int repomd_load(struct repoinfo *cinfo, Pool **sigpoolp);
diff --git a/examples/solv/repoinfo_type_susetags.c b/examples/solv/repoinfo_type_susetags.c
new file mode 100644
index 0000000..596171c
--- /dev/null
+++ b/examples/solv/repoinfo_type_susetags.c
@@ -0,0 +1,274 @@
+#ifdef ENABLE_SUSEREPO
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_content.h"
+#include "repo_susetags.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_susetags.h"
+
+/* susetags helpers */
+
+static const char *
+susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
+{
+ Pool *pool = repo->pool;
+ Dataiterator di;
+ const char *filename;
+
+ filename = 0;
+ *chksump = 0;
+ *chksumtypep = 0;
+ dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
+ dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
+ if (dataiterator_step(&di))
+ {
+ dataiterator_setpos_parent(&di);
+ *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
+ filename = what;
+ }
+ dataiterator_free(&di);
+ if (filename && !*chksumtypep)
+ {
+ printf("no %s file checksum!\n", what);
+ filename = 0;
+ }
+ return filename;
+}
+
+void
+susetags_add_ext(Repo *repo, Repodata *data)
+{
+ Pool *pool = repo->pool;
+ Dataiterator di;
+ char ext[3];
+ Id handle, filechksumtype;
+ const unsigned char *filechksum;
+
+ dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
+ dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
+ while (dataiterator_step(&di))
+ {
+ if (strncmp(di.kv.str, "packages.", 9) != 0)
+ continue;
+ if (!strcmp(di.kv.str + 9, "gz"))
+ continue;
+ if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
+ continue;
+ ext[0] = di.kv.str[9];
+ ext[1] = di.kv.str[10];
+ ext[2] = 0;
+ if (!strcmp(ext, "en"))
+ continue;
+ if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
+ continue;
+ handle = repodata_new_handle(data);
+ repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
+ if (filechksumtype)
+ repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
+ add_ext_keys(data, handle, ext);
+ repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+ }
+ dataiterator_free(&di);
+}
+
+int
+susetags_load_ext(Repo *repo, Repodata *data)
+{
+ const char *filename, *descrdir;
+ Id defvendor;
+ char ext[3];
+ FILE *fp;
+ struct repoinfo *cinfo;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ int flags;
+
+ cinfo = repo->appdata;
+ filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
+ if (!filename)
+ return 0;
+ /* susetags load */
+ ext[0] = filename[9];
+ ext[1] = filename[10];
+ ext[2] = 0;
+ printf("[%s:%s", repo->name, ext);
+ if (usecachedrepo(cinfo, ext, 0))
+ {
+ printf(" cached]\n"); fflush(stdout);
+ return 1;
+ }
+ printf(" fetching]\n"); fflush(stdout);
+ defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+ descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
+ if (!descrdir)
+ descrdir = "suse/setup/descr";
+ filechksumtype = 0;
+ filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 0)) == 0)
+ return 0;
+ flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+ if (strcmp(ext, "DL") != 0)
+ flags |= REPO_LOCALPOOL;
+ if (repo_add_susetags(repo, fp, defvendor, ext, flags))
+ {
+ fclose(fp);
+ printf("%s\n", pool_errstr(repo->pool));
+ return 0;
+ }
+ fclose(fp);
+ writecachedrepo(cinfo, ext, data);
+ return 1;
+}
+
+int
+susetags_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+ Repo *repo = cinfo->repo;
+ Pool *pool = repo->pool;
+ Repodata *data;
+ const char *filename;
+ const unsigned char *filechksum;
+ Id filechksumtype;
+ FILE *fp;
+ const char *descrdir;
+ int defvendor;
+
+ printf("susetags repo '%s':", cinfo->alias);
+ fflush(stdout);
+ descrdir = 0;
+ defvendor = 0;
+ if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
+ {
+ printf(" no content file\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+ cinfo->cookieset = 1;
+ if (usecachedrepo(cinfo, 0, 1))
+ {
+ printf(" cached\n");
+ fclose(fp);
+ return 1;
+ }
+ if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", sigpoolp))
+ {
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ if (repo_add_content(repo, fp, 0))
+ {
+ printf("content: %s\n", pool_errstr(pool));
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ fclose(fp);
+ defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+ descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
+ if (!descrdir)
+ descrdir = "suse/setup/descr";
+ filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
+ if (!filename)
+ filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
+ if (!filename)
+ {
+ printf(" no packages file entry, skipped\n");
+ cinfo->incomplete = 1;
+ return 0;
+ }
+ printf(" fetching\n");
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) == 0)
+ {
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
+ {
+ printf("packages: %s\n", pool_errstr(pool));
+ fclose(fp);
+ cinfo->incomplete = 1;
+ return 0; /* hopeless */
+ }
+ fclose(fp);
+ /* add default language */
+ filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
+ if (!filename)
+ filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
+ if (filename)
+ {
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
+ {
+ printf("packages.en: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+ }
+ filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
+ if (filename)
+ {
+ if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ char pbuf[256];
+ while (fgets(pbuf, sizeof(pbuf), fp))
+ {
+ int l = strlen(pbuf);
+ FILE *fp2;
+ if (l && pbuf[l - 1] == '\n')
+ pbuf[--l] = 0;
+ if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
+ continue;
+ filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
+ if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
+ {
+ printf("%s: %s\n", pbuf, pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp2);
+ }
+ }
+ fclose(fp);
+ }
+ }
+#ifdef ENABLE_APPDATA
+ filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
+ if (!filename)
+ filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
+ if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+ {
+ if (repo_add_appdata(repo, fp, 0))
+ {
+ printf("appdata: %s\n", pool_errstr(pool));
+ cinfo->incomplete = 1;
+ }
+ fclose(fp);
+ }
+#endif
+ repo_internalize(repo);
+ data = repo_add_repodata(repo, 0);
+ susetags_add_ext(repo, data);
+ repodata_internalize(data);
+ writecachedrepo(cinfo, 0, 0);
+ repodata_create_stubs(repo_last_repodata(repo));
+ return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_susetags.h b/examples/solv/repoinfo_type_susetags.h
new file mode 100644
index 0000000..2ad5778
--- /dev/null
+++ b/examples/solv/repoinfo_type_susetags.h
@@ -0,0 +1,2 @@
+extern int susetags_load_ext(Repo *repo, Repodata *data);
+extern int susetags_load(struct repoinfo *cinfo, Pool **sigpoolp);
diff --git a/examples/solv/solv.c b/examples/solv/solv.c
new file mode 100644
index 0000000..d624bf6
--- /dev/null
+++ b/examples/solv/solv.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) 2009-2015, SUSE LLC.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* solv, a little software installer demoing the sat solver library */
+
+/* things it does:
+ * - understands globs for package names / dependencies
+ * - understands .arch suffix
+ * - installation of commandline packages
+ * - repository data caching
+ * - on demand loading of secondary repository data
+ * - gpg and checksum verification
+ * - file conflicts
+ * - deltarpm support
+ * - fastestmirror implementation
+ *
+ * things available in the library but missing from solv:
+ * - vendor policy loading
+ * - multi version handling
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "poolarch.h"
+#include "evr.h"
+#include "selection.h"
+#include "repo.h"
+#include "solver.h"
+#include "solverdebug.h"
+#include "transaction.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+
+#if defined(ENABLE_RPMDB)
+#include "fileprovides.h"
+#include "fileconflicts.h"
+#include "deltarpm.h"
+#endif
+#if defined(SUSE) || defined(FEDORA)
+#include "patchjobs.h"
+#endif
+
+void
+setarch(Pool *pool)
+{
+ struct utsname un;
+ if (uname(&un))
+ {
+ perror("uname");
+ exit(1);
+ }
+ pool_setarch(pool, un.machine);
+}
+
+
+int
+yesno(const char *str)
+{
+ char inbuf[128], *ip;
+
+ for (;;)
+ {
+ printf("%s", str);
+ fflush(stdout);
+ *inbuf = 0;
+ if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ while (*ip == ' ' || *ip == '\t')
+ ip++;
+ if (*ip == 'q')
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ if (*ip == 'y' || *ip == 'n')
+ return *ip == 'y' ? 1 : 0;
+ }
+}
+
+#ifdef SUSE
+static Id
+nscallback(Pool *pool, void *data, Id name, Id evr)
+{
+#if 0
+ if (name == NAMESPACE_LANGUAGE)
+ {
+ if (!strcmp(pool_id2str(pool, evr), "ja"))
+ return 1;
+ if (!strcmp(pool_id2str(pool, evr), "de"))
+ return 1;
+ if (!strcmp(pool_id2str(pool, evr), "en"))
+ return 1;
+ if (!strcmp(pool_id2str(pool, evr), "en_US"))
+ return 1;
+ }
+#endif
+ return 0;
+}
+#endif
+
+#ifdef SUSE
+static void
+add_autopackages(Pool *pool)
+{
+ int i;
+ Repo *repo;
+ FOR_REPOS(i, repo)
+ repo_add_autopattern(repo, 0);
+}
+#endif
+
+#ifdef SUSE
+static void
+showdiskusagechanges(Transaction *trans)
+{
+ DUChanges duc[4];
+ int i;
+
+ /* XXX: use mountpoints here */
+ memset(duc, 0, sizeof(duc));
+ duc[0].path = "/";
+ duc[1].path = "/usr/share/man";
+ duc[2].path = "/sbin";
+ duc[3].path = "/etc";
+ transaction_calc_duchanges(trans, duc, 4);
+ for (i = 0; i < 4; i++)
+ printf("duchanges %s: %d K %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
+}
+#endif
+
+static Id
+find_repo(const char *name, Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
+{
+ const char *rp;
+ int i;
+
+ for (rp = name; *rp; rp++)
+ if (*rp <= '0' || *rp >= '9')
+ break;
+ if (!*rp)
+ {
+ /* repo specified by number */
+ int rnum = atoi(name);
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ if (!cinfo->enabled || !cinfo->repo)
+ continue;
+ if (--rnum == 0)
+ return cinfo->repo->repoid;
+ }
+ }
+ else
+ {
+ /* repo specified by alias */
+ Repo *repo;
+ FOR_REPOS(i, repo)
+ {
+ if (!strcasecmp(name, repo->name))
+ return repo->repoid;
+ }
+ }
+ return 0;
+}
+
+
+#define MODE_LIST 0
+#define MODE_INSTALL 1
+#define MODE_ERASE 2
+#define MODE_UPDATE 3
+#define MODE_DISTUPGRADE 4
+#define MODE_VERIFY 5
+#define MODE_PATCH 6
+#define MODE_INFO 7
+#define MODE_REPOLIST 8
+#define MODE_SEARCH 9
+
+void
+usage(int r)
+{
+ fprintf(stderr, "Usage: solv COMMAND <select>\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " dist-upgrade: replace installed packages with\n");
+ fprintf(stderr, " versions from the repositories\n");
+ fprintf(stderr, " erase: erase installed packages\n");
+ fprintf(stderr, " info: display package information\n");
+ fprintf(stderr, " install: install packages\n");
+ fprintf(stderr, " list: list packages\n");
+ fprintf(stderr, " repos: list enabled repositories\n");
+ fprintf(stderr, " search: search name/summary/description\n");
+ fprintf(stderr, " update: update installed packages\n");
+ fprintf(stderr, " verify: check dependencies of installed packages\n");
+#if defined(SUSE) || defined(FEDORA)
+ fprintf(stderr, " patch: install newest maintenance updates\n");
+#endif
+ fprintf(stderr, "\n");
+ exit(r);
+}
+
+int
+main(int argc, char **argv)
+{
+ Pool *pool;
+ Repo *commandlinerepo = 0;
+ Id *commandlinepkgs = 0;
+ Id p;
+ struct repoinfo *repoinfos, installedrepoinfo;
+ int nrepoinfos = 0;
+ int mainmode = 0, mode = 0;
+ int i, newpkgs;
+ Queue job, checkq;
+ Solver *solv = 0;
+ Transaction *trans;
+ FILE **newpkgsfps;
+ Queue repofilter;
+ Queue kindfilter;
+ Queue archfilter;
+ int archfilter_src = 0;
+ int cleandeps = 0;
+ int forcebest = 0;
+ char *rootdir = 0;
+ char *keyname = 0;
+ int debuglevel = 0;
+
+ argc--;
+ argv++;
+ while (argc && !strcmp(argv[0], "-d"))
+ {
+ debuglevel++;
+ argc--;
+ argv++;
+ }
+ if (!argv[0])
+ usage(1);
+ if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
+ {
+ mainmode = MODE_INSTALL;
+ mode = SOLVER_INSTALL;
+ }
+#if defined(SUSE) || defined(FEDORA)
+ else if (!strcmp(argv[0], "patch"))
+ {
+ mainmode = MODE_PATCH;
+ mode = SOLVER_INSTALL;
+ }
+#endif
+ else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
+ {
+ mainmode = MODE_ERASE;
+ mode = SOLVER_ERASE;
+ }
+ else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
+ {
+ mainmode = MODE_LIST;
+ mode = 0;
+ }
+ else if (!strcmp(argv[0], "info"))
+ {
+ mainmode = MODE_INFO;
+ mode = 0;
+ }
+ else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
+ {
+ mainmode = MODE_SEARCH;
+ mode = 0;
+ }
+ else if (!strcmp(argv[0], "verify"))
+ {
+ mainmode = MODE_VERIFY;
+ mode = SOLVER_VERIFY;
+ }
+ else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
+ {
+ mainmode = MODE_UPDATE;
+ mode = SOLVER_UPDATE;
+ }
+ else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
+ {
+ mainmode = MODE_DISTUPGRADE;
+ mode = SOLVER_DISTUPGRADE;
+ }
+ else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
+ {
+ mainmode = MODE_REPOLIST;
+ mode = 0;
+ }
+ else
+ usage(1);
+
+ for (;;)
+ {
+ if (argc > 2 && !strcmp(argv[1], "--root"))
+ {
+ rootdir = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc > 1 && !strcmp(argv[1], "--clean"))
+ {
+ cleandeps = 1;
+ argc--;
+ argv++;
+ }
+ else if (argc > 1 && !strcmp(argv[1], "--best"))
+ {
+ forcebest = 1;
+ argc--;
+ argv++;
+ }
+ if (argc > 2 && !strcmp(argv[1], "--keyname"))
+ {
+ keyname = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+ else
+ break;
+ }
+
+ set_userhome();
+ pool = pool_create();
+ pool_set_rootdir(pool, rootdir);
+
+#if 0
+ {
+ const char *langs[] = {"de_DE", "de", "en"};
+ pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
+ }
+#endif
+
+ pool_setloadcallback(pool, load_stub, 0);
+#ifdef SUSE
+ pool->nscallback = nscallback;
+#endif
+ if (debuglevel)
+ pool_setdebuglevel(pool, debuglevel);
+ setarch(pool);
+ pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
+ repoinfos = read_repoinfos(pool, &nrepoinfos);
+ sort_repoinfos(repoinfos, nrepoinfos);
+
+ if (mainmode == MODE_REPOLIST)
+ {
+ int j = 1;
+ for (i = 0; i < nrepoinfos; i++)
+ {
+ struct repoinfo *cinfo = repoinfos + i;
+ if (!cinfo->enabled)
+ continue;
+ printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
+ }
+ exit(0);
+ }
+ memset(&installedrepoinfo, 0, sizeof(installedrepoinfo));
+ if (!read_installed_repo(&installedrepoinfo, pool))
+ exit(1);
+ read_repos(pool, repoinfos, nrepoinfos);
+
+ /* setup filters */
+ queue_init(&repofilter);
+ queue_init(&kindfilter);
+ queue_init(&archfilter);
+ while (argc > 1)
+ {
+ if (!strcmp(argv[1], "-i"))
+ {
+ queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
+ argc--;
+ argv++;
+ }
+ else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
+ {
+ Id repoid = find_repo(argv[2], pool, repoinfos, nrepoinfos);
+ if (!repoid)
+ {
+ fprintf(stderr, "%s: no such repo\n", argv[2]);
+ exit(1);
+ }
+ /* SETVENDOR is actually wrong but useful */
+ queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc > 2 && !strcmp(argv[1], "--arch"))
+ {
+ if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
+ archfilter_src = 1;
+ queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
+ argc -= 2;
+ argv += 2;
+ }
+ else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
+ {
+ const char *kind = argv[2];
+ if (!strcmp(kind, "srcpackage"))
+ {
+ /* hey! should use --arch! */
+ queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
+ archfilter_src = 1;
+ argc -= 2;
+ argv += 2;
+ continue;
+ }
+ if (!strcmp(kind, "package"))
+ kind = "";
+ if (!strcmp(kind, "all"))
+ queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
+ else
+ queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
+ argc -= 2;
+ argv += 2;
+ }
+ else
+ break;
+ }
+
+ if (mainmode == MODE_SEARCH)
+ {
+ Queue sel, q;
+ Dataiterator di;
+ if (argc != 2)
+ usage(1);
+ pool_createwhatprovides(pool);
+ queue_init(&sel);
+ dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
+ dataiterator_set_keyname(&di, SOLVABLE_NAME);
+ dataiterator_set_search(&di, 0, 0);
+ while (dataiterator_step(&di))
+ queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+ dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
+ dataiterator_set_search(&di, 0, 0);
+ while (dataiterator_step(&di))
+ queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+ dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
+ dataiterator_set_search(&di, 0, 0);
+ while (dataiterator_step(&di))
+ queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+ dataiterator_free(&di);
+ if (repofilter.count)
+ selection_filter(pool, &sel, &repofilter);
+
+ queue_init(&q);
+ selection_solvables(pool, &sel, &q);
+ queue_free(&sel);
+ for (i = 0; i < q.count; i++)
+ {
+ Solvable *s = pool_id2solvable(pool, q.elements[i]);
+ printf(" - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
+ }
+ queue_free(&q);
+ exit(0);
+ }
+
+ /* process command line packages */
+ if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
+ {
+ for (i = 1; i < argc; i++)
+ {
+ if (!is_cmdline_package((const char *)argv[i]))
+ continue;
+ if (access(argv[i], R_OK))
+ {
+ perror(argv[i]);
+ exit(1);
+ }
+ if (!commandlinepkgs)
+ commandlinepkgs = solv_calloc(argc, sizeof(Id));
+ if (!commandlinerepo)
+ commandlinerepo = repo_create(pool, "@commandline");
+ p = add_cmdline_package(commandlinerepo, (const char *)argv[i]);
+ if (!p)
+ {
+ fprintf(stderr, "could not add '%s'\n", argv[i]);
+ exit(1);
+ }
+ commandlinepkgs[i] = p;
+ }
+ if (commandlinerepo)
+ repo_internalize(commandlinerepo);
+ }
+
+#if defined(ENABLE_RPMDB)
+ if (pool->disttype == DISTTYPE_RPM)
+ addfileprovides(pool);
+#endif
+#ifdef SUSE
+ add_autopackages(pool);
+#endif
+ pool_createwhatprovides(pool);
+
+ if (keyname)
+ keyname = solv_dupjoin("solvable:", keyname, 0);
+ queue_init(&job);
+ for (i = 1; i < argc; i++)
+ {
+ Queue job2;
+ int flags, rflags;
+
+ if (commandlinepkgs && commandlinepkgs[i])
+ {
+ queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
+ continue;
+ }
+ queue_init(&job2);
+ flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
+ flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
+ if (kindfilter.count)
+ flags |= SELECTION_SKIP_KIND;
+ if (mode == MODE_LIST || archfilter_src)
+ flags |= SELECTION_WITH_SOURCE;
+ if (argv[i][0] == '/')
+ flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
+ if (!keyname)
+ rflags = selection_make(pool, &job2, argv[i], flags);
+ else
+ rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
+ if (repofilter.count)
+ selection_filter(pool, &job2, &repofilter);
+ if (archfilter.count)
+ selection_filter(pool, &job2, &archfilter);
+ if (kindfilter.count)
+ selection_filter(pool, &job2, &kindfilter);
+ if (!job2.count)
+ {
+ flags |= SELECTION_NOCASE;
+ if (!keyname)
+ rflags = selection_make(pool, &job2, argv[i], flags);
+ else
+ rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
+ if (repofilter.count)
+ selection_filter(pool, &job2, &repofilter);
+ if (archfilter.count)
+ selection_filter(pool, &job2, &archfilter);
+ if (kindfilter.count)
+ selection_filter(pool, &job2, &kindfilter);
+ if (job2.count)
+ printf("[ignoring case for '%s']\n", argv[i]);
+ }
+ if (!job2.count)
+ {
+ fprintf(stderr, "nothing matches '%s'\n", argv[i]);
+ exit(1);
+ }
+ if (rflags & SELECTION_FILELIST)
+ printf("[using file list match for '%s']\n", argv[i]);
+ if (rflags & SELECTION_PROVIDES)
+ printf("[using capability match for '%s']\n", argv[i]);
+ queue_insertn(&job, job.count, job2.count, job2.elements);
+ queue_free(&job2);
+ }
+ keyname = solv_free(keyname);
+
+ if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
+ {
+ queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
+ if (repofilter.count)
+ selection_filter(pool, &job, &repofilter);
+ if (archfilter.count)
+ selection_filter(pool, &job, &archfilter);
+ if (kindfilter.count)
+ selection_filter(pool, &job, &kindfilter);
+ }
+ queue_free(&repofilter);
+ queue_free(&archfilter);
+ queue_free(&kindfilter);
+
+ if (!job.count && mainmode != MODE_PATCH)
+ {
+ printf("no package matched\n");
+ exit(1);
+ }
+
+ if (mainmode == MODE_LIST || mainmode == MODE_INFO)
+ {
+ /* list mode, no solver needed */
+ Queue q;
+ queue_init(&q);
+ for (i = 0; i < job.count; i += 2)
+ {
+ int j;
+ queue_empty(&q);
+ pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
+ for (j = 0; j < q.count; j++)
+ {
+ Solvable *s = pool_id2solvable(pool, q.elements[j]);
+ if (mainmode == MODE_INFO)
+ {
+ const char *str;
+ printf("Name: %s\n", pool_solvable2str(pool, s));
+ printf("Repo: %s\n", s->repo->name);
+ printf("Summary: %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
+ str = solvable_lookup_str(s, SOLVABLE_URL);
+ if (str)
+ printf("Url: %s\n", str);
+ str = solvable_lookup_str(s, SOLVABLE_LICENSE);
+ if (str)
+ printf("License: %s\n", str);
+ printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
+ printf("\n");
+ }
+ else
+ {
+#if 1
+ const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
+#else
+ const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
+#endif
+ printf(" - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
+ if (sum)
+ printf(" %s\n", sum);
+ }
+ }
+ }
+ queue_free(&q);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(0);
+ }
+
+#if defined(SUSE) || defined(FEDORA)
+ if (mainmode == MODE_PATCH)
+ add_patchjobs(pool, &job);
+#endif
+
+ // add mode
+ for (i = 0; i < job.count; i += 2)
+ {
+ job.elements[i] |= mode;
+ if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
+ job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
+ if (cleandeps)
+ job.elements[i] |= SOLVER_CLEANDEPS;
+ if (forcebest)
+ job.elements[i] |= SOLVER_FORCEBEST;
+ }
+
+ // multiversion test
+ // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
+ // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
+ // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
+#if 0
+ queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
+ queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
+#endif
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
+rerunsolver:
+#endif
+ solv = solver_create(pool);
+ solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
+#ifdef FEDORA
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
+#endif
+ if (mainmode == MODE_ERASE)
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1); /* don't nag */
+ solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
+
+ for (;;)
+ {
+ Id problem, solution;
+ int pcnt, scnt;
+
+ if (!solver_solve(solv, &job))
+ break;
+ pcnt = solver_problem_count(solv);
+ printf("Found %d problems:\n", pcnt);
+ for (problem = 1; problem <= pcnt; problem++)
+ {
+ int take = 0;
+ printf("Problem %d/%d:\n", problem, pcnt);
+ solver_printprobleminfo(solv, problem);
+ printf("\n");
+ scnt = solver_solution_count(solv, problem);
+ for (solution = 1; solution <= scnt; solution++)
+ {
+ printf("Solution %d:\n", solution);
+ solver_printsolution(solv, problem, solution);
+ printf("\n");
+ }
+ for (;;)
+ {
+ char inbuf[128], *ip;
+ printf("Please choose a solution: ");
+ fflush(stdout);
+ *inbuf = 0;
+ if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ while (*ip == ' ' || *ip == '\t')
+ ip++;
+ if (*ip >= '0' && *ip <= '9')
+ {
+ take = atoi(ip);
+ if (take >= 1 && take <= scnt)
+ break;
+ }
+ if (*ip == 's')
+ {
+ take = 0;
+ break;
+ }
+ if (*ip == 'q')
+ {
+ printf("Abort.\n");
+ exit(1);
+ }
+ }
+ if (!take)
+ continue;
+ solver_take_solution(solv, problem, take, &job);
+ }
+ }
+
+ trans = solver_create_transaction(solv);
+ if (!trans->steps.count)
+ {
+ printf("Nothing to do.\n");
+ transaction_free(trans);
+ solver_free(solv);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(1);
+ }
+
+ /* display transaction to the user and ask for confirmation */
+ printf("\n");
+ printf("Transaction summary:\n\n");
+ transaction_print(trans);
+#if defined(SUSE)
+ showdiskusagechanges(trans);
+#endif
+ printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
+ printf("\n");
+
+ if (!yesno("OK to continue (y/n)? "))
+ {
+ printf("Abort.\n");
+ transaction_free(trans);
+ solver_free(solv);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(1);
+ }
+
+ /* download all new packages */
+ queue_init(&checkq);
+ newpkgs = transaction_installedresult(trans, &checkq);
+ newpkgsfps = 0;
+ if (newpkgs)
+ {
+ int downloadsize = 0;
+ for (i = 0; i < newpkgs; i++)
+ {
+ Solvable *s;
+
+ p = checkq.elements[i];
+ s = pool_id2solvable(pool, p);
+ downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
+ }
+ printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
+ newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
+ for (i = 0; i < newpkgs; i++)
+ {
+ const char *loc;
+ Solvable *s;
+ struct repoinfo *cinfo;
+
+ p = checkq.elements[i];
+ s = pool_id2solvable(pool, p);
+ if (s->repo == commandlinerepo)
+ {
+ loc = solvable_lookup_location(s, 0);
+ if (!loc)
+ continue;
+ if (!(newpkgsfps[i] = fopen(loc, "r")))
+ {
+ perror(loc);
+ exit(1);
+ }
+ putchar('.');
+ continue;
+ }
+ cinfo = s->repo->appdata;
+ if (!cinfo || cinfo->type == TYPE_INSTALLED)
+ {
+ printf("%s: no repository information\n", s->repo->name);
+ exit(1);
+ }
+ loc = solvable_lookup_location(s, 0);
+ if (!loc)
+ continue; /* pseudo package? */
+#if defined(ENABLE_RPMDB)
+ if (pool->installed && pool->installed->nsolvables)
+ {
+ if ((newpkgsfps[i] = trydeltadownload(s, loc)) != 0)
+ {
+ putchar('d');
+ fflush(stdout);
+ continue; /* delta worked! */
+ }
+ }
+#endif
+ if ((newpkgsfps[i] = downloadpackage(s, loc)) == 0)
+ {
+ printf("\n%s: %s not found in repository\n", s->repo->name, loc);
+ exit(1);
+ }
+ putchar('.');
+ fflush(stdout);
+ }
+ putchar('\n');
+ }
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+ /* check for file conflicts */
+ if (newpkgs)
+ {
+ Queue conflicts;
+ queue_init(&conflicts);
+ if (checkfileconflicts(pool, &checkq, newpkgs, newpkgsfps, &conflicts))
+ {
+ if (yesno("Re-run solver (y/n/q)? "))
+ {
+ for (i = 0; i < newpkgs; i++)
+ if (newpkgsfps[i])
+ fclose(newpkgsfps[i]);
+ newpkgsfps = solv_free(newpkgsfps);
+ solver_free(solv);
+ solv = 0;
+ pool_add_fileconflicts_deps(pool, &conflicts);
+ queue_free(&conflicts);
+ goto rerunsolver;
+ }
+ }
+ queue_free(&conflicts);
+ }
+#endif
+
+ /* and finally commit the transaction */
+ printf("Committing transaction:\n\n");
+ transaction_order(trans, 0);
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ int j;
+ FILE *fp;
+ Id type;
+
+ p = trans->steps.elements[i];
+ type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
+ switch(type)
+ {
+ case SOLVER_TRANSACTION_ERASE:
+ printf("erase %s\n", pool_solvid2str(pool, p));
+ commit_transactionelement(pool, type, p, 0);
+ break;
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ printf("install %s\n", pool_solvid2str(pool, p));
+ for (j = 0; j < newpkgs; j++)
+ if (checkq.elements[j] == p)
+ break;
+ fp = j < newpkgs ? newpkgsfps[j] : 0;
+ if (!fp)
+ continue;
+ commit_transactionelement(pool, type, p, fp);
+ fclose(fp);
+ newpkgsfps[j] = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < newpkgs; i++)
+ if (newpkgsfps[i])
+ fclose(newpkgsfps[i]);
+ solv_free(newpkgsfps);
+ queue_free(&checkq);
+ transaction_free(trans);
+ solver_free(solv);
+ queue_free(&job);
+ pool_free(pool);
+ free_repoinfos(repoinfos, nrepoinfos);
+ solv_free(commandlinepkgs);
+ exit(0);
+}
diff --git a/ext/repo_autopattern.c b/ext/repo_autopattern.c
index 7edfc6c..4c767e1 100644
--- a/ext/repo_autopattern.c
+++ b/ext/repo_autopattern.c
@@ -265,8 +265,13 @@ repo_add_autopattern(Repo *repo, int flags)
repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
else if (!strcmp(pn, "pattern-order()") && evr)
repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
- else if (!strcmp(pn, "pattern-visible()") && !evr)
- repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
+ else if (!strcmp(pn, "pattern-visible()"))
+ {
+ if (!evr)
+ repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
+ else
+ repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE, newname);
+ }
}
}
queue_free(&patq);
diff --git a/ext/repo_mdk.c b/ext/repo_mdk.c
index 3d0a91d..345d416 100644
--- a/ext/repo_mdk.c
+++ b/ext/repo_mdk.c
@@ -123,6 +123,8 @@ repo_add_mdk(Repo *repo, FILE *fp, int flags)
s->provides = parse_deps(s, buf + 10, 0);
else if (!strncmp(buf + 1, "requires@", 9))
s->requires = parse_deps(s, buf + 10, SOLVABLE_PREREQMARKER);
+ else if (!strncmp(buf + 1, "recommends@", 11))
+ s->recommends = parse_deps(s, buf + 10, 0);
else if (!strncmp(buf + 1, "suggests@", 9))
s->suggests = parse_deps(s, buf + 10, 0);
else if (!strncmp(buf + 1, "obsoletes@", 10))
diff --git a/ext/testcase.c b/ext/testcase.c
index e4346fe..3c40451 100644
--- a/ext/testcase.c
+++ b/ext/testcase.c
@@ -33,18 +33,19 @@ static struct job2str {
Id job;
const char *str;
} job2str[] = {
- { SOLVER_NOOP, "noop" },
- { SOLVER_INSTALL, "install" },
- { SOLVER_ERASE, "erase" },
- { SOLVER_UPDATE, "update" },
- { SOLVER_WEAKENDEPS, "weakendeps" },
- { SOLVER_MULTIVERSION, "multiversion" },
- { SOLVER_MULTIVERSION, "noobsoletes" }, /* old name */
- { SOLVER_LOCK, "lock" },
- { SOLVER_DISTUPGRADE, "distupgrade" },
- { SOLVER_VERIFY, "verify" },
- { SOLVER_DROP_ORPHANED, "droporphaned" },
- { SOLVER_USERINSTALLED, "userinstalled" },
+ { SOLVER_NOOP, "noop" },
+ { SOLVER_INSTALL, "install" },
+ { SOLVER_ERASE, "erase" },
+ { SOLVER_UPDATE, "update" },
+ { SOLVER_WEAKENDEPS, "weakendeps" },
+ { SOLVER_MULTIVERSION, "multiversion" },
+ { SOLVER_MULTIVERSION, "noobsoletes" }, /* old name */
+ { SOLVER_LOCK, "lock" },
+ { SOLVER_DISTUPGRADE, "distupgrade" },
+ { SOLVER_VERIFY, "verify" },
+ { SOLVER_DROP_ORPHANED, "droporphaned" },
+ { SOLVER_USERINSTALLED, "userinstalled" },
+ { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
{ 0, 0 }
};
diff --git a/package/libsolv.changes b/package/libsolv.changes
index b98c3ba..5ddad42 100644
--- a/package/libsolv.changes
+++ b/package/libsolv.changes
@@ -1,10 +1,21 @@
-------------------------------------------------------------------
+Fri Sep 25 11:54:02 CEST 2015 - mls@suse.de
+
+- support a generic string for pattern-visible() [bnc#900769]
+- add a SOLVER_ALLOWUNINSTALL job type
+- add ordercycle introspection
+- fix mkmask handling of a zero size
+- support 'recommends' in repo_mdk.c
+- support filelist parsing in installcheck
+- bump version to 0.6.13
+
+-------------------------------------------------------------------
Tue Sep 1 13:37:11 CEST 2015 - mls@suse.de
- added tcl bindings
- improve debian ar archive handling
- bindings: set the CLOEXEC flags in xfopen
-- bindings: support testcase writing
+- bindings: support testcase writing [bnc#946752]
- support REL_ELSE as evr of REL_COND
- bump version to 0.6.12
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5d7190a..a2c0098 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -17,7 +17,7 @@ SET (libsolv_SRCS
bitmap.c poolarch.c poolvendor.c poolid.c strpool.c dirpool.c
solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c
queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
- transaction.c rules.c problems.c linkedpkg.c cplxdeps.c
+ transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
chksum.c md5.c sha1.c sha2.c solvversion.c selection.c)
SET (libsolv_HEADERS
diff --git a/src/hash.h b/src/hash.h
index 1290afa..4f595bb 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -73,14 +73,14 @@ relhash(Id name, Id evr, int flags)
/* compute bitmask for value
- * returns smallest (2^n-1) > 2 * num
+ * returns smallest (2^n-1) > 2 * num + 3
*
* used for Hashtable 'modulo' operation
*/
static inline Hashval
mkmask(unsigned int num)
{
- num *= 2;
+ num = num * 2 + 3;
while (num & (num - 1))
num &= num - 1;
return num * 2 - 1;
diff --git a/src/libsolv.ver b/src/libsolv.ver
index 91186a7..9e47117 100644
--- a/src/libsolv.ver
+++ b/src/libsolv.ver
@@ -408,6 +408,8 @@ SOLV_1.0 {
transaction_obs_pkg;
transaction_order;
transaction_order_add_choices;
+ transaction_order_get_cycle;
+ transaction_order_get_cycleids;
transaction_print;
transaction_type;
local:
diff --git a/src/order.c b/src/order.c
new file mode 100644
index 0000000..d560865
--- /dev/null
+++ b/src/order.c
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (c) 2007-2015, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * order.c
+ *
+ * Transaction ordering
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "transaction.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+
+struct _TransactionElement {
+ Id p; /* solvable id */
+ Id edges; /* pointer into edges data */
+ Id mark;
+};
+
+struct _TransactionOrderdata {
+ struct _TransactionElement *tes;
+ int ntes;
+ Id *invedgedata;
+ int ninvedgedata;
+ Queue *cycles;
+};
+
+#define TYPE_BROKEN (1<<0)
+#define TYPE_CON (1<<1)
+
+#define TYPE_REQ_P (1<<2)
+#define TYPE_PREREQ_P (1<<3)
+
+#define TYPE_REQ (1<<4)
+#define TYPE_PREREQ (1<<5)
+
+#define TYPE_CYCLETAIL (1<<16)
+#define TYPE_CYCLEHEAD (1<<17)
+
+#define EDGEDATA_BLOCK 127
+
+void
+transaction_clone_orderdata(Transaction *trans, Transaction *srctrans)
+{
+ struct _TransactionOrderdata *od = srctrans->orderdata;
+ if (!od)
+ return;
+ trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
+ trans->orderdata->tes = solv_memdup2(od->tes, od->ntes, sizeof(*od->tes));
+ trans->orderdata->ntes = od->ntes;
+ trans->orderdata->invedgedata = solv_memdup2(od->invedgedata, od->ninvedgedata, sizeof(Id));
+ trans->orderdata->ninvedgedata = od->ninvedgedata;
+ if (od->cycles)
+ {
+ trans->orderdata->cycles = solv_calloc(1, sizeof(Queue));
+ queue_init_clone(trans->orderdata->cycles, od->cycles);
+ }
+}
+
+void
+transaction_free_orderdata(Transaction *trans)
+{
+ if (trans->orderdata)
+ {
+ struct _TransactionOrderdata *od = trans->orderdata;
+ od->tes = solv_free(od->tes);
+ od->invedgedata = solv_free(od->invedgedata);
+ if (od->cycles)
+ {
+ queue_free(od->cycles);
+ od->cycles = solv_free(od->cycles);
+ }
+ trans->orderdata = solv_free(trans->orderdata);
+ }
+}
+
+struct orderdata {
+ Transaction *trans;
+ struct _TransactionElement *tes;
+ int ntes;
+ Id *edgedata;
+ int nedgedata;
+ Id *invedgedata;
+
+ Queue cycles;
+ Queue cyclesdata;
+ int ncycles;
+};
+
+static int
+addteedge(struct orderdata *od, int from, int to, int type)
+{
+ int i;
+ struct _TransactionElement *te;
+
+ if (from == to)
+ return 0;
+
+ /* printf("edge %d(%s) -> %d(%s) type %x\n", from, pool_solvid2str(pool, od->tes[from].p), to, pool_solvid2str(pool, od->tes[to].p), type); */
+
+ te = od->tes + from;
+ for (i = te->edges; od->edgedata[i]; i += 2)
+ if (od->edgedata[i] == to)
+ break;
+ /* test of brokenness */
+ if (type == TYPE_BROKEN)
+ return od->edgedata[i] && (od->edgedata[i + 1] & TYPE_BROKEN) != 0 ? 1 : 0;
+ if (od->edgedata[i])
+ {
+ od->edgedata[i + 1] |= type;
+ return 0;
+ }
+ if (i + 1 == od->nedgedata)
+ {
+ /* printf("tail add %d\n", i - te->edges); */
+ if (!i)
+ te->edges = ++i;
+ od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
+ }
+ else
+ {
+ /* printf("extend %d\n", i - te->edges); */
+ od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
+ if (i > te->edges)
+ memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
+ i = od->nedgedata + (i - te->edges);
+ te->edges = od->nedgedata;
+ }
+ od->edgedata[i] = to;
+ od->edgedata[i + 1] = type;
+ od->edgedata[i + 2] = 0; /* end marker */
+ od->nedgedata = i + 3;
+ return 0;
+}
+
+static int
+addedge(struct orderdata *od, Id from, Id to, int type)
+{
+ Transaction *trans = od->trans;
+ Pool *pool = trans->pool;
+ Solvable *s;
+ struct _TransactionElement *te;
+ int i;
+
+ /* printf("addedge %d %d type %d\n", from, to, type); */
+ s = pool->solvables + from;
+ if (s->repo == pool->installed && trans->transaction_installed[from - pool->installed->start])
+ {
+ /* obsolete, map to install */
+ if (trans->transaction_installed[from - pool->installed->start] > 0)
+ from = trans->transaction_installed[from - pool->installed->start];
+ else
+ {
+ int ret = 0;
+ Queue ti;
+ Id tibuf[5];
+
+ queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
+ transaction_all_obs_pkgs(trans, from, &ti);
+ for (i = 0; i < ti.count; i++)
+ ret |= addedge(od, ti.elements[i], to, type);
+ queue_free(&ti);
+ return ret;
+ }
+ }
+ s = pool->solvables + to;
+ if (s->repo == pool->installed && trans->transaction_installed[to - pool->installed->start])
+ {
+ /* obsolete, map to install */
+ if (trans->transaction_installed[to - pool->installed->start] > 0)
+ to = trans->transaction_installed[to - pool->installed->start];
+ else
+ {
+ int ret = 0;
+ Queue ti;
+ Id tibuf[5];
+
+ queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
+ transaction_all_obs_pkgs(trans, to, &ti);
+ for (i = 0; i < ti.count; i++)
+ ret |= addedge(od, from, ti.elements[i], type);
+ queue_free(&ti);
+ return ret;
+ }
+ }
+
+ /* map from/to to te numbers */
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (te->p == to)
+ break;
+ if (i == od->ntes)
+ return 0;
+ to = i;
+
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (te->p == from)
+ break;
+ if (i == od->ntes)
+ return 0;
+
+ return addteedge(od, i, to, type);
+}
+
+static inline int
+havescripts(Pool *pool, Id solvid)
+{
+ Solvable *s = pool->solvables + solvid;
+ const char *dep;
+ if (s->requires)
+ {
+ Id req, *reqp;
+ int inpre = 0;
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ {
+ inpre = 1;
+ continue;
+ }
+ if (!inpre)
+ continue;
+ dep = pool_id2str(pool, req);
+ if (*dep == '/' && strcmp(dep, "/sbin/ldconfig") != 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+addsolvableedges(struct orderdata *od, Solvable *s)
+{
+ Transaction *trans = od->trans;
+ Pool *pool = trans->pool;
+ Id req, *reqp, con, *conp;
+ Id p, p2, pp2;
+ int i, j, pre, numins;
+ Repo *installed = pool->installed;
+ Solvable *s2;
+ Queue reqq;
+ int provbyinst;
+
+#if 0
+ printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
+#endif
+ p = s - pool->solvables;
+ queue_init(&reqq);
+ if (s->requires)
+ {
+ reqp = s->repo->idarraydata + s->requires;
+ pre = TYPE_REQ;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ {
+ pre = TYPE_PREREQ;
+ continue;
+ }
+#if 0
+ if (pre != TYPE_PREREQ && installed && s->repo == installed)
+ {
+ /* ignore normal requires if we're getting obsoleted */
+ if (trans->transaction_installed[p - pool->installed->start])
+ continue;
+ }
+#endif
+ queue_empty(&reqq);
+ numins = 0; /* number of packages to be installed providing it */
+ provbyinst = 0; /* provided by kept package */
+ FOR_PROVIDES(p2, pp2, req)
+ {
+ s2 = pool->solvables + p2;
+ if (p2 == p)
+ {
+ reqq.count = 0; /* self provides */
+ break;
+ }
+ if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
+ {
+ provbyinst = 1;
+#if 0
+ printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
+ reqq.count = 0; /* provided by package that stays installed */
+ break;
+#else
+ continue;
+#endif
+ }
+ if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
+ continue; /* package stays uninstalled */
+
+ if (s->repo == installed)
+ {
+ /* s gets uninstalled */
+ queue_pushunique(&reqq, p2);
+ if (s2->repo != installed)
+ numins++;
+ }
+ else
+ {
+ if (s2->repo == installed)
+ continue; /* s2 gets uninstalled */
+ queue_pushunique(&reqq, p2);
+ }
+ }
+ if (provbyinst)
+ {
+ /* prune to harmless ->inst edges */
+ for (i = j = 0; i < reqq.count; i++)
+ if (pool->solvables[reqq.elements[i]].repo != installed)
+ reqq.elements[j++] = reqq.elements[i];
+ reqq.count = j;
+ }
+
+ if (numins && reqq.count)
+ {
+ if (s->repo == installed)
+ {
+ for (i = 0; i < reqq.count; i++)
+ {
+ if (pool->solvables[reqq.elements[i]].repo == installed)
+ {
+ for (j = 0; j < reqq.count; j++)
+ {
+ if (pool->solvables[reqq.elements[j]].repo != installed)
+ {
+ if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
+ continue; /* no self edge */
+#if 0
+ printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
+#endif
+ addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+ }
+ }
+ }
+ }
+ }
+ /* no mixed types, remove all deps on uninstalls */
+ for (i = j = 0; i < reqq.count; i++)
+ if (pool->solvables[reqq.elements[i]].repo != installed)
+ reqq.elements[j++] = reqq.elements[i];
+ reqq.count = j;
+ }
+ if (!reqq.count)
+ continue;
+ for (i = 0; i < reqq.count; i++)
+ {
+ p2 = reqq.elements[i];
+ if (pool->solvables[p2].repo != installed)
+ {
+ /* all elements of reqq are installs, thus have different TEs */
+ if (pool->solvables[p].repo != installed)
+ {
+#if 0
+ printf("add inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p, p2, pre);
+ }
+ else
+ {
+#if 0
+ printf("add uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p, p2, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+ }
+ }
+ else
+ {
+ if (s->repo != installed)
+ continue; /* no inst->uninst edges, please! */
+
+ /* uninst -> uninst edge. Those make trouble. Only add if we must */
+ if (trans->transaction_installed[p - installed->start] && !havescripts(pool, p))
+ {
+ /* p is obsoleted by another package and has no scripts */
+ /* we assume that the obsoletor is good enough to replace p */
+ continue;
+ }
+#if 0
+ printf("add uninst->uninst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p2, p, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+ }
+ }
+ }
+ }
+ if (s->conflicts)
+ {
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p)
+ continue;
+ s2 = pool->solvables + p2;
+ if (!s2->repo)
+ continue;
+ if (s->repo == installed)
+ {
+ if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
+ {
+ /* deinstall p before installing p2 */
+#if 0
+ printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, con), pool_solvid2str(pool, p));
+#endif
+ addedge(od, p2, p, TYPE_CON);
+ }
+ }
+ else
+ {
+ if (s2->repo == installed && MAPTST(&trans->transactsmap, p2))
+ {
+ /* deinstall p2 before installing p */
+#if 0
+ printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, con), pool_solvid2str(pool, p2));
+#endif
+ addedge(od, p, p2, TYPE_CON);
+ }
+ }
+
+ }
+ }
+ }
+ if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
+ {
+ /* we're getting deinstalled/updated. Try to do this before our
+ * triggers are hit */
+ for (i = 0; i < reqq.count; i++)
+ {
+ Id tri = reqq.elements[i];
+ FOR_PROVIDES(p2, pp2, tri)
+ {
+ if (p2 == p)
+ continue;
+ s2 = pool->solvables + p2;
+ if (!s2->repo)
+ continue;
+ if (s2->name == s->name)
+ continue; /* obsoleted anyway */
+ if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
+ {
+ /* deinstall/update p before installing p2 */
+#if 0
+ printf("add trigger uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, tri), pool_solvid2str(pool, p));
+#endif
+ addedge(od, p2, p, TYPE_CON);
+ }
+ }
+ }
+ }
+ queue_free(&reqq);
+}
+
+
+/* break an edge in a cycle */
+static void
+breakcycle(struct orderdata *od, Id *cycle)
+{
+ Pool *pool = od->trans->pool;
+ Id ddegmin, ddegmax, ddeg;
+ int k, l;
+ struct _TransactionElement *te;
+
+ l = 0;
+ ddegmin = ddegmax = 0;
+ for (k = 0; cycle[k + 1]; k += 2)
+ {
+ ddeg = od->edgedata[cycle[k + 1] + 1];
+ if (ddeg > ddegmax)
+ ddegmax = ddeg;
+ if (!k || ddeg < ddegmin)
+ {
+ l = k;
+ ddegmin = ddeg;
+ continue;
+ }
+ if (ddeg == ddegmin)
+ {
+ if (havescripts(pool, od->tes[cycle[l]].p) && !havescripts(pool, od->tes[cycle[k]].p))
+ {
+ /* prefer k, as l comes from a package with contains scriptlets */
+ l = k;
+ continue;
+ }
+ /* same edge value, check for prereq */
+ }
+ }
+
+ /* record brkoen cycle starting with the tail */
+ queue_push(&od->cycles, od->cyclesdata.count); /* offset into data */
+ queue_push(&od->cycles, k / 2); /* cycle elements */
+ queue_push(&od->cycles, od->edgedata[cycle[l + 1] + 1]); /* broken edge */
+ queue_push(&od->cycles, (ddegmax << 16) | ddegmin); /* max/min values */
+ od->ncycles++;
+ for (k = l;;)
+ {
+ k += 2;
+ if (!cycle[k + 1])
+ k = 0;
+ queue_push(&od->cyclesdata, cycle[k]);
+ if (k == l)
+ break;
+ }
+ queue_push(&od->cyclesdata, 0); /* mark end */
+
+ /* break that edge */
+ od->edgedata[cycle[l + 1] + 1] |= TYPE_BROKEN;
+
+#if 1
+ if (ddegmin < TYPE_REQ)
+ return;
+#endif
+
+ /* cycle recorded, print it */
+ if (ddegmin >= TYPE_REQ && (ddegmax & TYPE_PREREQ) != 0)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "CRITICAL ");
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycle: --> ");
+ for (k = 0; cycle[k + 1]; k += 2)
+ {
+ te = od->tes + cycle[k];
+ if ((od->edgedata[cycle[k + 1] + 1] & TYPE_BROKEN) != 0)
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%s ##%x##> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
+ else
+ POOL_DEBUG(SOLV_DEBUG_STATS, "%s --%x--> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "\n");
+}
+
+static inline void
+dump_tes(struct orderdata *od)
+{
+ Pool *pool = od->trans->pool;
+ int i, j;
+ Queue obsq;
+ struct _TransactionElement *te, *te2;
+
+ queue_init(&obsq);
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ {
+ Solvable *s = pool->solvables + te->p;
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "TE %4d: %c%s\n", i, s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+ if (s->repo != pool->installed)
+ {
+ queue_empty(&obsq);
+ transaction_all_obs_pkgs(od->trans, te->p, &obsq);
+ for (j = 0; j < obsq.count; j++)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " -%s\n", pool_solvid2str(pool, obsq.elements[j]));
+ }
+ for (j = te->edges; od->edgedata[j]; j += 2)
+ {
+ te2 = od->tes + od->edgedata[j];
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) == 0)
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " --%x--> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
+ else
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " ##%x##> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
+ }
+ }
+}
+
+static void
+reachable(struct orderdata *od, Id i)
+{
+ struct _TransactionElement *te = od->tes + i;
+ int j, k;
+
+ if (te->mark != 0)
+ return;
+ te->mark = 1;
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (!od->tes[k].mark)
+ reachable(od, k);
+ if (od->tes[k].mark == 2)
+ {
+ te->mark = 2;
+ return;
+ }
+ }
+ te->mark = -1;
+}
+
+static void
+addcycleedges(struct orderdata *od, Id *cycle, Queue *todo)
+{
+#if 0
+ Transaction *trans = od->trans;
+ Pool *pool = trans->pool;
+#endif
+ struct _TransactionElement *te;
+ int i, j, k, tail;
+ int head;
+
+#if 0
+ printf("addcycleedges\n");
+ for (i = 0; (j = cycle[i]) != 0; i++)
+ printf("cycle %s\n", pool_solvid2str(pool, od->tes[j].p));
+#endif
+
+ /* first add all the tail cycle edges */
+
+ /* see what we can reach from the cycle */
+ queue_empty(todo);
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ te->mark = 0;
+ for (i = 0; (j = cycle[i]) != 0; i++)
+ {
+ od->tes[j].mark = -1;
+ queue_push(todo, j);
+ }
+ while (todo->count)
+ {
+ i = queue_pop(todo);
+ te = od->tes + i;
+ if (te->mark > 0)
+ continue;
+ te->mark = te->mark < 0 ? 2 : 1;
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (od->tes[k].mark > 0)
+ continue; /* no need to visit again */
+ queue_push(todo, k);
+ }
+ }
+ /* now all cycle TEs are marked with 2, all TEs reachable
+ * from the cycle are marked with 1 */
+ tail = cycle[0];
+ od->tes[tail].mark = 1; /* no need to add edges */
+
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ {
+ if (te->mark)
+ continue; /* reachable from cycle */
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (od->tes[k].mark != 2)
+ continue;
+ /* We found an edge to the cycle. Add an extra edge to the tail */
+ /* the TE was not reachable, so we're not creating a new cycle! */
+#if 0
+ printf("adding TO TAIL cycle edge %d->%d %s->%s!\n", i, tail, pool_solvid2str(pool, od->tes[i].p), pool_solvid2str(pool, od->tes[tail].p));
+#endif
+ j -= te->edges; /* in case we move */
+ addteedge(od, i, tail, TYPE_CYCLETAIL);
+ j += te->edges;
+ break; /* one edge is enough */
+ }
+ }
+
+ /* now add all head cycle edges */
+
+ /* reset marks */
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ te->mark = 0;
+ head = 0;
+ for (i = 0; (j = cycle[i]) != 0; i++)
+ {
+ head = j;
+ od->tes[j].mark = 2;
+ }
+ /* first the head to save some time */
+ te = od->tes + head;
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (!od->tes[k].mark)
+ reachable(od, k);
+ if (od->tes[k].mark == -1)
+ od->tes[k].mark = -2; /* no need for another edge */
+ }
+ for (i = 0; cycle[i] != 0; i++)
+ {
+ if (cycle[i] == head)
+ break;
+ te = od->tes + cycle[i];
+ for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+ {
+ if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ /* see if we can reach a cycle TE from k */
+ if (!od->tes[k].mark)
+ reachable(od, k);
+ if (od->tes[k].mark == -1)
+ {
+#if 0
+ printf("adding FROM HEAD cycle edge %d->%d %s->%s [%s]!\n", head, k, pool_solvid2str(pool, od->tes[head].p), pool_solvid2str(pool, od->tes[k].p), pool_solvid2str(pool, od->tes[cycle[i]].p));
+#endif
+ addteedge(od, head, k, TYPE_CYCLEHEAD);
+ od->tes[k].mark = -2; /* no need to add that one again */
+ }
+ }
+ }
+}
+
+void
+transaction_order(Transaction *trans, int flags)
+{
+ Pool *pool = trans->pool;
+ Queue *tr = &trans->steps;
+ Repo *installed = pool->installed;
+ Id p;
+ Solvable *s;
+ int i, j, k, numte, numedge;
+ struct orderdata od;
+ struct _TransactionElement *te;
+ Queue todo, obsq, samerepoq, uninstq;
+ int cycstart, cycel;
+ Id *cycle;
+ int oldcount;
+ int start, now;
+ Repo *lastrepo;
+ int lastmedia;
+ Id *temedianr;
+
+ start = now = solv_timems(0);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "ordering transaction\n");
+ /* free old data if present */
+ if (trans->orderdata)
+ {
+ struct _TransactionOrderdata *od = trans->orderdata;
+ od->tes = solv_free(od->tes);
+ od->invedgedata = solv_free(od->invedgedata);
+ trans->orderdata = solv_free(trans->orderdata);
+ }
+
+ /* create a transaction element for every active component */
+ numte = 0;
+ for (i = 0; i < tr->count; i++)
+ {
+ p = tr->elements[i];
+ s = pool->solvables + p;
+ if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
+ continue;
+ numte++;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "transaction elements: %d\n", numte);
+ if (!numte)
+ return; /* nothing to do... */
+
+ numte++; /* leave first one zero */
+ memset(&od, 0, sizeof(od));
+ od.trans = trans;
+ od.ntes = numte;
+ od.tes = solv_calloc(numte, sizeof(*od.tes));
+ od.edgedata = solv_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
+ od.edgedata[0] = 0;
+ od.nedgedata = 1;
+ queue_init(&od.cycles);
+
+ /* initialize TEs */
+ for (i = 0, te = od.tes + 1; i < tr->count; i++)
+ {
+ p = tr->elements[i];
+ s = pool->solvables + p;
+ if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
+ continue;
+ te->p = p;
+ te++;
+ }
+
+ /* create dependency graph */
+ for (i = 0; i < tr->count; i++)
+ addsolvableedges(&od, pool->solvables + tr->elements[i]);
+
+ /* count edges */
+ numedge = 0;
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ numedge++;
+ POOL_DEBUG(SOLV_DEBUG_STATS, "edges: %d, edge space: %d\n", numedge, od.nedgedata / 2);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "edge creation took %d ms\n", solv_timems(now));
+
+#if 0
+ dump_tes(&od);
+#endif
+
+ now = solv_timems(0);
+ /* kill all cycles */
+ queue_init(&todo);
+ for (i = numte - 1; i > 0; i--)
+ queue_push(&todo, i);
+
+ while (todo.count)
+ {
+ i = queue_pop(&todo);
+ /* printf("- look at TE %d\n", i); */
+ if (i < 0)
+ {
+ i = -i;
+ od.tes[i].mark = 2; /* done with that one */
+ continue;
+ }
+ te = od.tes + i;
+ if (te->mark == 2)
+ continue; /* already finished before */
+ if (te->mark == 0)
+ {
+ int edgestovisit = 0;
+ /* new node, visit edges */
+ for (j = te->edges; (k = od.edgedata[j]) != 0; j += 2)
+ {
+ if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ if (od.tes[k].mark == 2)
+ continue; /* no need to visit again */
+ if (!edgestovisit++)
+ queue_push(&todo, -i); /* end of edges marker */
+ queue_push(&todo, k);
+ }
+ if (!edgestovisit)
+ te->mark = 2; /* no edges, done with that one */
+ else
+ te->mark = 1; /* under investigation */
+ continue;
+ }
+ /* oh no, we found a cycle */
+ /* find start of cycle node (<0) */
+ for (j = todo.count - 1; j >= 0; j--)
+ if (todo.elements[j] == -i)
+ break;
+ assert(j >= 0);
+ cycstart = j;
+ /* build te/edge chain */
+ k = cycstart;
+ for (j = k; j < todo.count; j++)
+ if (todo.elements[j] < 0)
+ todo.elements[k++] = -todo.elements[j];
+ cycel = k - cycstart;
+ assert(cycel > 1);
+ /* make room for edges, two extra element for cycle loop + terminating 0 */
+ while (todo.count < cycstart + 2 * cycel + 2)
+ queue_push(&todo, 0);
+ cycle = todo.elements + cycstart;
+ cycle[cycel] = i; /* close the loop */
+ cycle[2 * cycel + 1] = 0; /* terminator */
+ for (k = cycel; k > 0; k--)
+ {
+ cycle[k * 2] = cycle[k];
+ te = od.tes + cycle[k - 1];
+ assert(te->mark == 1);
+ te->mark = 0; /* reset investigation marker */
+ /* printf("searching for edge from %d to %d\n", cycle[k - 1], cycle[k]); */
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ if (od.edgedata[j] == cycle[k])
+ break;
+ assert(od.edgedata[j]);
+ cycle[k * 2 - 1] = j;
+ }
+ /* now cycle looks like this: */
+ /* te1 edge te2 edge te3 ... teN edge te1 0 */
+ breakcycle(&od, cycle);
+ /* restart with start of cycle */
+ todo.count = cycstart + 1;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycles broken: %d\n", od.ncycles);
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycle breaking took %d ms\n", solv_timems(now));
+
+ now = solv_timems(0);
+ /* now go through all broken cycles and create cycle edges to help
+ the ordering */
+ for (i = od.cycles.count - 4; i >= 0; i -= 4)
+ {
+ if (od.cycles.elements[i + 2] >= TYPE_REQ)
+ addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
+ }
+ for (i = od.cycles.count - 4; i >= 0; i -= 4)
+ {
+ if (od.cycles.elements[i + 2] < TYPE_REQ)
+ addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "cycle edge creation took %d ms\n", solv_timems(now));
+
+#if 0
+ dump_tes(&od);
+#endif
+ /* all edges are finally set up and there are no cycles, now the easy part.
+ * Create an ordered transaction */
+ now = solv_timems(0);
+ /* first invert all edges */
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ te->mark = 1; /* term 0 */
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ {
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ {
+ if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ od.tes[od.edgedata[j]].mark++;
+ }
+ }
+ j = 1;
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ {
+ te->mark += j;
+ j = te->mark;
+ }
+ POOL_DEBUG(SOLV_DEBUG_STATS, "invedge space: %d\n", j + 1);
+ od.invedgedata = solv_calloc(j + 1, sizeof(Id));
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ {
+ for (j = te->edges; od.edgedata[j]; j += 2)
+ {
+ if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+ continue;
+ od.invedgedata[--od.tes[od.edgedata[j]].mark] = i;
+ }
+ }
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ te->edges = te->mark; /* edges now points into invedgedata */
+ od.edgedata = solv_free(od.edgedata);
+ od.nedgedata = j + 1;
+
+ /* now the final ordering */
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ te->mark = 0;
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ for (j = te->edges; od.invedgedata[j]; j++)
+ od.tes[od.invedgedata[j]].mark++;
+
+ queue_init(&samerepoq);
+ queue_init(&uninstq);
+ queue_empty(&todo);
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ if (te->mark == 0)
+ {
+ if (installed && pool->solvables[te->p].repo == installed)
+ queue_push(&uninstq, i);
+ else
+ queue_push(&todo, i);
+ }
+ assert(todo.count > 0 || uninstq.count > 0);
+ oldcount = tr->count;
+ queue_empty(tr);
+
+ queue_init(&obsq);
+
+ lastrepo = 0;
+ lastmedia = 0;
+ temedianr = solv_calloc(numte, sizeof(Id));
+ for (i = 1; i < numte; i++)
+ {
+ Solvable *s = pool->solvables + od.tes[i].p;
+ if (installed && s->repo == installed)
+ j = 1;
+ else
+ j = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
+ temedianr[i] = j;
+ }
+ for (;;)
+ {
+ /* select an TE i */
+ if (uninstq.count)
+ i = queue_shift(&uninstq);
+ else if (samerepoq.count)
+ i = queue_shift(&samerepoq);
+ else if (todo.count)
+ {
+ /* find next repo/media */
+ for (j = 0; j < todo.count; j++)
+ {
+ if (!j || temedianr[todo.elements[j]] < lastmedia)
+ {
+ i = j;
+ lastmedia = temedianr[todo.elements[j]];
+ }
+ }
+ lastrepo = pool->solvables[od.tes[todo.elements[i]].p].repo;
+
+ /* move all matching TEs to samerepoq */
+ for (i = j = 0; j < todo.count; j++)
+ {
+ int k = todo.elements[j];
+ if (temedianr[k] == lastmedia && pool->solvables[od.tes[k].p].repo == lastrepo)
+ queue_push(&samerepoq, k);
+ else
+ todo.elements[i++] = k;
+ }
+ todo.count = i;
+
+ assert(samerepoq.count);
+ i = queue_shift(&samerepoq);
+ }
+ else
+ break;
+
+ te = od.tes + i;
+ queue_push(tr, te->p);
+#if 0
+printf("do %s [%d]\n", pool_solvid2str(pool, te->p), temedianr[i]);
+#endif
+ s = pool->solvables + te->p;
+ for (j = te->edges; od.invedgedata[j]; j++)
+ {
+ struct _TransactionElement *te2 = od.tes + od.invedgedata[j];
+ assert(te2->mark > 0);
+ if (--te2->mark == 0)
+ {
+ Solvable *s = pool->solvables + te2->p;
+#if 0
+printf("free %s [%d]\n", pool_solvid2str(pool, te2->p), temedianr[od.invedgedata[j]]);
+#endif
+ if (installed && s->repo == installed)
+ queue_push(&uninstq, od.invedgedata[j]);
+ else if (s->repo == lastrepo && temedianr[od.invedgedata[j]] == lastmedia)
+ queue_push(&samerepoq, od.invedgedata[j]);
+ else
+ queue_push(&todo, od.invedgedata[j]);
+ }
+ }
+ }
+ solv_free(temedianr);
+ queue_free(&todo);
+ queue_free(&samerepoq);
+ queue_free(&uninstq);
+ queue_free(&obsq);
+ for (i = 1, te = od.tes + i; i < numte; i++, te++)
+ assert(te->mark == 0);
+
+ /* add back obsoleted packages */
+ transaction_add_obsoleted(trans);
+ assert(tr->count == oldcount);
+
+ POOL_DEBUG(SOLV_DEBUG_STATS, "creating new transaction took %d ms\n", solv_timems(now));
+ POOL_DEBUG(SOLV_DEBUG_STATS, "transaction ordering took %d ms\n", solv_timems(start));
+
+ if ((flags & (SOLVER_TRANSACTION_KEEP_ORDERDATA | SOLVER_TRANSACTION_KEEP_ORDERCYCLES)) != 0)
+ {
+ struct _TransactionOrderdata *tod;
+ trans->orderdata = tod = solv_calloc(1, sizeof(*trans->orderdata));
+ if ((flags & SOLVER_TRANSACTION_KEEP_ORDERCYCLES) != 0)
+ {
+ Queue *cycles = tod->cycles = solv_calloc(1, sizeof(Queue));
+ queue_init_clone(cycles, &od.cyclesdata);
+ /* map from tes to packages */
+ for (i = 0; i < cycles->count; i++)
+ if (cycles->elements[i])
+ cycles->elements[i] = od.tes[cycles->elements[i]].p;
+ queue_insertn(cycles, cycles->count, od.cycles.count, od.cycles.elements);
+ queue_push(cycles, od.cycles.count / 4);
+ }
+ if ((flags & SOLVER_TRANSACTION_KEEP_ORDERDATA) != 0)
+ {
+ tod->tes = od.tes;
+ tod->ntes = numte;
+ tod->invedgedata = od.invedgedata;
+ tod->ninvedgedata = od.nedgedata;
+ od.tes = 0;
+ od.invedgedata = 0;
+ }
+ }
+ solv_free(od.tes);
+ solv_free(od.invedgedata);
+ queue_free(&od.cycles);
+ queue_free(&od.cyclesdata);
+}
+
+
+int
+transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices)
+{
+ int i, j;
+ struct _TransactionOrderdata *od = trans->orderdata;
+ struct _TransactionElement *te;
+
+ if (!od)
+ return choices->count;
+ if (!chosen)
+ {
+ /* initialization step */
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ te->mark = 0;
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ {
+ for (j = te->edges; od->invedgedata[j]; j++)
+ od->tes[od->invedgedata[j]].mark++;
+ }
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (!te->mark)
+ queue_push(choices, te->p);
+ return choices->count;
+ }
+ for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+ if (te->p == chosen)
+ break;
+ if (i == od->ntes)
+ return choices->count;
+ if (te->mark > 0)
+ {
+ /* hey! out-of-order installation! */
+ te->mark = -1;
+ }
+ for (j = te->edges; od->invedgedata[j]; j++)
+ {
+ te = od->tes + od->invedgedata[j];
+ assert(te->mark > 0 || te->mark == -1);
+ if (te->mark > 0 && --te->mark == 0)
+ queue_push(choices, te->p);
+ }
+ return choices->count;
+}
+
+void
+transaction_add_obsoleted(Transaction *trans)
+{
+ Pool *pool = trans->pool;
+ Repo *installed = pool->installed;
+ Id p;
+ Solvable *s;
+ int i, j, k, max;
+ Map done;
+ Queue obsq, *steps;
+
+ if (!installed || !trans->steps.count)
+ return;
+ /* calculate upper bound */
+ max = 0;
+ FOR_REPO_SOLVABLES(installed, p, s)
+ if (MAPTST(&trans->transactsmap, p))
+ max++;
+ if (!max)
+ return;
+ /* make room */
+ steps = &trans->steps;
+ queue_insertn(steps, 0, max, 0);
+
+ /* now add em */
+ map_init(&done, installed->end - installed->start);
+ queue_init(&obsq);
+ for (j = 0, i = max; i < steps->count; i++)
+ {
+ p = trans->steps.elements[i];
+ if (pool->solvables[p].repo == installed)
+ {
+ if (!trans->transaction_installed[p - pool->installed->start])
+ trans->steps.elements[j++] = p;
+ continue;
+ }
+ trans->steps.elements[j++] = p;
+ queue_empty(&obsq);
+ transaction_all_obs_pkgs(trans, p, &obsq);
+ for (k = 0; k < obsq.count; k++)
+ {
+ p = obsq.elements[k];
+ assert(p >= installed->start && p < installed->end);
+ if (!MAPTST(&trans->transactsmap, p)) /* just in case */
+ continue;
+ if (MAPTST(&done, p - installed->start))
+ continue;
+ MAPSET(&done, p - installed->start);
+ trans->steps.elements[j++] = p;
+ }
+ }
+
+ /* free unneeded space */
+ queue_truncate(steps, j);
+ map_free(&done);
+ queue_free(&obsq);
+}
+
+static void
+transaction_check_pkg(Transaction *trans, Id tepkg, Id pkg, Map *ins, Map *seen, int onlyprereq, Id noconfpkg, int depth)
+{
+ Pool *pool = trans->pool;
+ Id p, pp;
+ Solvable *s;
+ int good;
+
+ if (MAPTST(seen, pkg))
+ return;
+ MAPSET(seen, pkg);
+ s = pool->solvables + pkg;
+#if 0
+ printf("- %*s%c%s\n", depth * 2, "", s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+#endif
+ if (s->requires)
+ {
+ Id req, *reqp;
+ int inpre = 0;
+ reqp = s->repo->idarraydata + s->requires;
+ while ((req = *reqp++) != 0)
+ {
+ if (req == SOLVABLE_PREREQMARKER)
+ {
+ inpre = 1;
+ continue;
+ }
+ if (onlyprereq && !inpre)
+ continue;
+ if (!strncmp(pool_id2str(pool, req), "rpmlib(", 7))
+ continue;
+ good = 0;
+ /* first check kept packages, then freshly installed, then not yet uninstalled */
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (!MAPTST(ins, p))
+ continue;
+ if (MAPTST(&trans->transactsmap, p))
+ continue;
+ good++;
+ transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+ }
+ if (!good)
+ {
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (!MAPTST(ins, p))
+ continue;
+ if (pool->solvables[p].repo == pool->installed)
+ continue;
+ good++;
+ transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+ }
+ }
+ if (!good)
+ {
+ FOR_PROVIDES(p, pp, req)
+ {
+ if (!MAPTST(ins, p))
+ continue;
+ good++;
+ transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+ }
+ }
+ if (!good)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RESULT, " %c%s: nothing provides %s needed by %c%s\n", pool->solvables[tepkg].repo == pool->installed ? '-' : '+', pool_solvid2str(pool, tepkg), pool_dep2str(pool, req), s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+ }
+ }
+ }
+}
+
+void
+transaction_check_order(Transaction *trans)
+{
+ Pool *pool = trans->pool;
+ Solvable *s;
+ Id p, lastins;
+ Map ins, seen;
+ int i;
+
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "\nchecking transaction order...\n");
+ map_init(&ins, pool->nsolvables);
+ map_init(&seen, pool->nsolvables);
+ if (pool->installed)
+ FOR_REPO_SOLVABLES(pool->installed, p, s)
+ MAPSET(&ins, p);
+ lastins = 0;
+ for (i = 0; i < trans->steps.count; i++)
+ {
+ p = trans->steps.elements[i];
+ s = pool->solvables + p;
+ if (s->repo != pool->installed)
+ lastins = p;
+ if (s->repo != pool->installed)
+ MAPSET(&ins, p);
+ if (havescripts(pool, p))
+ {
+ MAPZERO(&seen);
+ transaction_check_pkg(trans, p, p, &ins, &seen, 1, lastins, 0);
+ }
+ if (s->repo == pool->installed)
+ MAPCLR(&ins, p);
+ }
+ map_free(&seen);
+ map_free(&ins);
+ POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction order check done.\n");
+}
+
+void
+transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity)
+{
+ struct _TransactionOrderdata *od = trans->orderdata;
+ Queue *cq;
+ int i, cid, ncycles;
+
+ queue_empty(q);
+ if (!od || !od->cycles || !od->cycles->count)
+ return;
+ cq = od->cycles;
+ ncycles = cq->elements[cq->count - 1];
+ i = cq->count - 1 - ncycles * 4;
+ for (cid = 1; cid <= ncycles; cid++, i += 4)
+ {
+ if (minseverity)
+ {
+ int cmin = cq->elements[i + 3] & 0xffff;
+ int cmax = (cq->elements[i + 3] >> 16) & 0xffff;
+ if (minseverity >= SOLVER_ORDERCYCLE_NORMAL && cmin < TYPE_REQ)
+ continue;
+ if (minseverity >= SOLVER_ORDERCYCLE_CRITICAL && (cmax & TYPE_PREREQ) == 0)
+ continue;
+ }
+ queue_push(q, cid);
+ }
+}
+
+int
+transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q)
+{
+ struct _TransactionOrderdata *od = trans->orderdata;
+ Queue *cq;
+ int cmin, cmax, severity;
+ int ncycles;
+
+ queue_empty(q);
+ if (!od || !od->cycles || !od->cycles->count)
+ return SOLVER_ORDERCYCLE_HARMLESS;
+ cq = od->cycles;
+ ncycles = cq->elements[cq->count - 1];
+ if (cid < 1 || cid > ncycles)
+ return SOLVER_ORDERCYCLE_HARMLESS;
+ cid = cq->count - 1 - 4 * (ncycles - cid + 1);
+ cmin = cq->elements[cid + 3] & 0xffff;
+ cmax = (cq->elements[cid + 3] >> 16) & 0xffff;
+ if (cmin < TYPE_REQ)
+ severity = SOLVER_ORDERCYCLE_HARMLESS;
+ else if ((cmax & TYPE_PREREQ) == 0)
+ severity = SOLVER_ORDERCYCLE_NORMAL;
+ else
+ severity = SOLVER_ORDERCYCLE_CRITICAL;
+ if (q)
+ queue_insertn(q, 0, cq->elements[cid + 1], cq->elements + cq->elements[cid]);
+ return severity;
+}
+
diff --git a/src/solver.c b/src/solver.c
index b89e0d1..3358b6b 100644
--- a/src/solver.c
+++ b/src/solver.c
@@ -200,15 +200,25 @@ autouninstall(Solver *solv, Id *problem)
int lastfeature = 0, lastupdate = 0;
Id v;
Id extraflags = -1;
+ Map *m = 0;
+ if (!solv->allowuninstall && !solv->allowuninstall_all)
+ {
+ if (!solv->allowuninstallmap.size)
+ return 0; /* why did we get called? */
+ m = &solv->allowuninstallmap;
+ }
for (i = 0; (v = problem[i]) != 0; i++)
{
if (v < 0)
extraflags &= solv->job.elements[-v - 1];
if (v >= solv->updaterules && v < solv->updaterules_end)
{
+ Rule *r;
+ if (m && !MAPTST(m, v - solv->updaterules))
+ continue;
/* check if identical to feature rule, we don't like that */
- Rule *r = solv->rules + solv->featurerules + (v - solv->updaterules);
+ r = solv->rules + solv->featurerules + (v - solv->updaterules);
if (!r->p)
{
/* update rule == feature rule */
@@ -427,8 +437,16 @@ makeruledecisions(Solver *solv)
v = ri;
queue_push(&solv->problems, v);
queue_push(&solv->problems, 0);
- if (solv->allowuninstall && v >= solv->featurerules && v < solv->updaterules_end)
- solv->problems.count = oldproblemcount;
+ if (v >= solv->featurerules && v < solv->updaterules_end)
+ {
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ havedisabled = 1;
+ break; /* start over */
+ }
+ }
solver_disableproblem(solv, v);
havedisabled = 1;
break; /* start over */
@@ -458,8 +476,16 @@ makeruledecisions(Solver *solv)
v = ri;
queue_push(&solv->problems, v);
queue_push(&solv->problems, 0);
- if (solv->allowuninstall && v >= solv->featurerules && v < solv->updaterules_end)
- solv->problems.count = oldproblemcount;
+ if (v >= solv->featurerules && v < solv->updaterules_end)
+ {
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ havedisabled = 1;
+ break; /* start over */
+ }
+ }
solver_disableproblem(solv, v);
havedisabled = 1;
break; /* start over */
@@ -507,8 +533,13 @@ makeruledecisions(Solver *solv)
}
queue_push(&solv->problems, 0);
- if (solv->allowuninstall && (v = autouninstall(solv, solv->problems.elements + oldproblemcount + 1)) != 0)
- solv->problems.count = oldproblemcount;
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ havedisabled = 1;
+ break; /* start over */
+ }
for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
solver_disableproblem(solv, solv->problems.elements[i]);
@@ -1245,13 +1276,14 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
return 0;
}
- if (solv->allowuninstall && (v = autouninstall(solv, solv->problems.elements + oldproblemcount + 1)) != 0)
- {
- solv->problems.count = oldproblemcount;
- solv->learnt_pool.count = oldlearntpoolcount;
- solver_reset(solv);
- return 0;
- }
+ if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+ if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+ {
+ solv->problems.count = oldproblemcount;
+ solv->learnt_pool.count = oldlearntpoolcount;
+ solver_reset(solv);
+ return 0;
+ }
/* finish proof */
if (record_proof)
@@ -1672,6 +1704,7 @@ solver_free(Solver *solv)
map_free(&solv->dupinvolvedmap);
map_free(&solv->droporphanedmap);
map_free(&solv->cleandepsmap);
+ map_free(&solv->allowuninstallmap);
solv_free(solv->decisionmap);
solv_free(solv->rules);
@@ -3359,6 +3392,8 @@ solver_solve(Solver *solv, Queue *job)
map_zerosize(&solv->dupinvolvedmap);
solv->droporphanedmap_all = 0;
map_zerosize(&solv->droporphanedmap);
+ solv->allowuninstall_all = 0;
+ map_zerosize(&solv->allowuninstallmap);
map_zerosize(&solv->cleandepsmap);
map_zerosize(&solv->weakrulemap);
queue_empty(&solv->weakruleq);
@@ -3873,6 +3908,20 @@ solver_solve(Solver *solv, Queue *job)
case SOLVER_USERINSTALLED:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: user installed %s\n", solver_select2str(pool, select, what));
break;
+ case SOLVER_ALLOWUNINSTALL:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: allowuninstall %s\n", solver_select2str(pool, select, what));
+ if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
+ solv->allowuninstall_all = 1;
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!solv->allowuninstallmap.size)
+ map_grow(&solv->allowuninstallmap, installed->end - installed->start);
+ MAPSET(&solv->allowuninstallmap, p - installed->start);
+ }
+ break;
default:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n");
break;
@@ -5082,6 +5131,9 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
case SOLVER_USERINSTALLED:
strstart = "regard ", strend = " as userinstalled";
break;
+ case SOLVER_ALLOWUNINSTALL:
+ strstart = "allow deinstallation of ";
+ break;
default:
strstart = "unknown job ";
break;
diff --git a/src/solver.h b/src/solver.h
index 1a47ae0..2ae9c8d 100644
--- a/src/solver.h
+++ b/src/solver.h
@@ -195,6 +195,10 @@ struct _Solver {
int keep_orphans; /* how to treat orphans */
int break_orphans; /* how to treat orphans */
Queue *brokenorphanrules; /* broken rules of orphaned packages */
+
+ Map allowuninstallmap; /* ok to uninstall those */
+ int allowuninstall_all;
+
#endif /* LIBSOLV_INTERNAL */
};
@@ -223,7 +227,8 @@ typedef struct _Solver Solver;
#define SOLVER_DISTUPGRADE 0x0700
#define SOLVER_VERIFY 0x0800
#define SOLVER_DROP_ORPHANED 0x0900
-#define SOLVER_USERINSTALLED 0x0a00
+#define SOLVER_USERINSTALLED 0x0a00
+#define SOLVER_ALLOWUNINSTALL 0x0b00
#define SOLVER_JOBMASK 0xff00
diff --git a/src/transaction.c b/src/transaction.c
index 49e58e8..ffe1ec2 100644
--- a/src/transaction.c
+++ b/src/transaction.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2009, Novell Inc.
+ * Copyright (c) 2007-2015, SUSE LLC
*
* This program is licensed under the BSD license, read LICENSE.BSD
* for further information
@@ -842,33 +842,6 @@ transaction_calc_duchanges(Transaction *trans, DUChanges *mps, int nmps)
map_free(&installedmap);
}
-struct _TransactionElement {
- Id p; /* solvable id */
- Id edges; /* pointer into edges data */
- Id mark;
-};
-
-struct _TransactionOrderdata {
- struct _TransactionElement *tes;
- int ntes;
- Id *invedgedata;
- int ninvedgedata;
-};
-
-#define TYPE_BROKEN (1<<0)
-#define TYPE_CON (1<<1)
-
-#define TYPE_REQ_P (1<<2)
-#define TYPE_PREREQ_P (1<<3)
-
-#define TYPE_REQ (1<<4)
-#define TYPE_PREREQ (1<<5)
-
-#define TYPE_CYCLETAIL (1<<16)
-#define TYPE_CYCLEHEAD (1<<17)
-
-#define EDGEDATA_BLOCK 127
-
Transaction *
transaction_create(Pool *pool)
{
@@ -891,14 +864,7 @@ transaction_create_clone(Transaction *srctrans)
map_init_clone(&trans->transactsmap, &srctrans->transactsmap);
map_init_clone(&trans->multiversionmap, &srctrans->multiversionmap);
if (srctrans->orderdata)
- {
- struct _TransactionOrderdata *od = srctrans->orderdata;
- trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
- trans->orderdata->tes = solv_memdup2(od->tes, od->ntes, sizeof(*od->tes));
- trans->orderdata->ntes = od->ntes;
- trans->orderdata->invedgedata = solv_memdup2(od->invedgedata, od->ninvedgedata, sizeof(Id));
- trans->orderdata->ninvedgedata = od->ninvedgedata;
- }
+ transaction_clone_orderdata(trans, srctrans);
return trans;
}
@@ -910,1210 +876,8 @@ transaction_free(Transaction *trans)
trans->transaction_installed = solv_free(trans->transaction_installed);
map_free(&trans->transactsmap);
map_free(&trans->multiversionmap);
- transaction_free_orderdata(trans);
- free(trans);
-}
-
-void
-transaction_free_orderdata(Transaction *trans)
-{
if (trans->orderdata)
- {
- struct _TransactionOrderdata *od = trans->orderdata;
- od->tes = solv_free(od->tes);
- od->invedgedata = solv_free(od->invedgedata);
- trans->orderdata = solv_free(trans->orderdata);
- }
-}
-
-struct orderdata {
- Transaction *trans;
- struct _TransactionElement *tes;
- int ntes;
- Id *edgedata;
- int nedgedata;
- Id *invedgedata;
-
- Queue cycles;
- Queue cyclesdata;
- int ncycles;
-};
-
-static int
-addteedge(struct orderdata *od, int from, int to, int type)
-{
- int i;
- struct _TransactionElement *te;
-
- if (from == to)
- return 0;
-
- /* printf("edge %d(%s) -> %d(%s) type %x\n", from, pool_solvid2str(pool, od->tes[from].p), to, pool_solvid2str(pool, od->tes[to].p), type); */
-
- te = od->tes + from;
- for (i = te->edges; od->edgedata[i]; i += 2)
- if (od->edgedata[i] == to)
- break;
- /* test of brokenness */
- if (type == TYPE_BROKEN)
- return od->edgedata[i] && (od->edgedata[i + 1] & TYPE_BROKEN) != 0 ? 1 : 0;
- if (od->edgedata[i])
- {
- od->edgedata[i + 1] |= type;
- return 0;
- }
- if (i + 1 == od->nedgedata)
- {
- /* printf("tail add %d\n", i - te->edges); */
- if (!i)
- te->edges = ++i;
- od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
- }
- else
- {
- /* printf("extend %d\n", i - te->edges); */
- od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
- if (i > te->edges)
- memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
- i = od->nedgedata + (i - te->edges);
- te->edges = od->nedgedata;
- }
- od->edgedata[i] = to;
- od->edgedata[i + 1] = type;
- od->edgedata[i + 2] = 0; /* end marker */
- od->nedgedata = i + 3;
- return 0;
-}
-
-static int
-addedge(struct orderdata *od, Id from, Id to, int type)
-{
- Transaction *trans = od->trans;
- Pool *pool = trans->pool;
- Solvable *s;
- struct _TransactionElement *te;
- int i;
-
- /* printf("addedge %d %d type %d\n", from, to, type); */
- s = pool->solvables + from;
- if (s->repo == pool->installed && trans->transaction_installed[from - pool->installed->start])
- {
- /* obsolete, map to install */
- if (trans->transaction_installed[from - pool->installed->start] > 0)
- from = trans->transaction_installed[from - pool->installed->start];
- else
- {
- int ret = 0;
- Queue ti;
- Id tibuf[5];
-
- queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
- transaction_all_obs_pkgs(trans, from, &ti);
- for (i = 0; i < ti.count; i++)
- ret |= addedge(od, ti.elements[i], to, type);
- queue_free(&ti);
- return ret;
- }
- }
- s = pool->solvables + to;
- if (s->repo == pool->installed && trans->transaction_installed[to - pool->installed->start])
- {
- /* obsolete, map to install */
- if (trans->transaction_installed[to - pool->installed->start] > 0)
- to = trans->transaction_installed[to - pool->installed->start];
- else
- {
- int ret = 0;
- Queue ti;
- Id tibuf[5];
-
- queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
- transaction_all_obs_pkgs(trans, to, &ti);
- for (i = 0; i < ti.count; i++)
- ret |= addedge(od, from, ti.elements[i], type);
- queue_free(&ti);
- return ret;
- }
- }
-
- /* map from/to to te numbers */
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (te->p == to)
- break;
- if (i == od->ntes)
- return 0;
- to = i;
-
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (te->p == from)
- break;
- if (i == od->ntes)
- return 0;
-
- return addteedge(od, i, to, type);
-}
-
-static inline int
-havescripts(Pool *pool, Id solvid)
-{
- Solvable *s = pool->solvables + solvid;
- const char *dep;
- if (s->requires)
- {
- Id req, *reqp;
- int inpre = 0;
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- {
- inpre = 1;
- continue;
- }
- if (!inpre)
- continue;
- dep = pool_id2str(pool, req);
- if (*dep == '/' && strcmp(dep, "/sbin/ldconfig") != 0)
- return 1;
- }
- }
- return 0;
-}
-
-static void
-addsolvableedges(struct orderdata *od, Solvable *s)
-{
- Transaction *trans = od->trans;
- Pool *pool = trans->pool;
- Id req, *reqp, con, *conp;
- Id p, p2, pp2;
- int i, j, pre, numins;
- Repo *installed = pool->installed;
- Solvable *s2;
- Queue reqq;
- int provbyinst;
-
-#if 0
- printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
-#endif
- p = s - pool->solvables;
- queue_init(&reqq);
- if (s->requires)
- {
- reqp = s->repo->idarraydata + s->requires;
- pre = TYPE_REQ;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- {
- pre = TYPE_PREREQ;
- continue;
- }
-#if 0
- if (pre != TYPE_PREREQ && installed && s->repo == installed)
- {
- /* ignore normal requires if we're getting obsoleted */
- if (trans->transaction_installed[p - pool->installed->start])
- continue;
- }
-#endif
- queue_empty(&reqq);
- numins = 0; /* number of packages to be installed providing it */
- provbyinst = 0; /* provided by kept package */
- FOR_PROVIDES(p2, pp2, req)
- {
- s2 = pool->solvables + p2;
- if (p2 == p)
- {
- reqq.count = 0; /* self provides */
- break;
- }
- if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
- {
- provbyinst = 1;
-#if 0
- printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
- reqq.count = 0; /* provided by package that stays installed */
- break;
-#else
- continue;
-#endif
- }
- if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
- continue; /* package stays uninstalled */
-
- if (s->repo == installed)
- {
- /* s gets uninstalled */
- queue_pushunique(&reqq, p2);
- if (s2->repo != installed)
- numins++;
- }
- else
- {
- if (s2->repo == installed)
- continue; /* s2 gets uninstalled */
- queue_pushunique(&reqq, p2);
- }
- }
- if (provbyinst)
- {
- /* prune to harmless ->inst edges */
- for (i = j = 0; i < reqq.count; i++)
- if (pool->solvables[reqq.elements[i]].repo != installed)
- reqq.elements[j++] = reqq.elements[i];
- reqq.count = j;
- }
-
- if (numins && reqq.count)
- {
- if (s->repo == installed)
- {
- for (i = 0; i < reqq.count; i++)
- {
- if (pool->solvables[reqq.elements[i]].repo == installed)
- {
- for (j = 0; j < reqq.count; j++)
- {
- if (pool->solvables[reqq.elements[j]].repo != installed)
- {
- if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
- continue; /* no self edge */
-#if 0
- printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
-#endif
- addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
- }
- }
- }
- }
- }
- /* no mixed types, remove all deps on uninstalls */
- for (i = j = 0; i < reqq.count; i++)
- if (pool->solvables[reqq.elements[i]].repo != installed)
- reqq.elements[j++] = reqq.elements[i];
- reqq.count = j;
- }
- if (!reqq.count)
- continue;
- for (i = 0; i < reqq.count; i++)
- {
- p2 = reqq.elements[i];
- if (pool->solvables[p2].repo != installed)
- {
- /* all elements of reqq are installs, thus have different TEs */
- if (pool->solvables[p].repo != installed)
- {
-#if 0
- printf("add inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p, p2, pre);
- }
- else
- {
-#if 0
- printf("add uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p, p2, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
- }
- }
- else
- {
- if (s->repo != installed)
- continue; /* no inst->uninst edges, please! */
-
- /* uninst -> uninst edge. Those make trouble. Only add if we must */
- if (trans->transaction_installed[p - installed->start] && !havescripts(pool, p))
- {
- /* p is obsoleted by another package and has no scripts */
- /* we assume that the obsoletor is good enough to replace p */
- continue;
- }
-#if 0
- printf("add uninst->uninst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p2, p, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
- }
- }
- }
- }
- if (s->conflicts)
- {
- conp = s->repo->idarraydata + s->conflicts;
- while ((con = *conp++) != 0)
- {
- FOR_PROVIDES(p2, pp2, con)
- {
- if (p2 == p)
- continue;
- s2 = pool->solvables + p2;
- if (!s2->repo)
- continue;
- if (s->repo == installed)
- {
- if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
- {
- /* deinstall p before installing p2 */
-#if 0
- printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, con), pool_solvid2str(pool, p));
-#endif
- addedge(od, p2, p, TYPE_CON);
- }
- }
- else
- {
- if (s2->repo == installed && MAPTST(&trans->transactsmap, p2))
- {
- /* deinstall p2 before installing p */
-#if 0
- printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, con), pool_solvid2str(pool, p2));
-#endif
- addedge(od, p, p2, TYPE_CON);
- }
- }
-
- }
- }
- }
- if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
- {
- /* we're getting deinstalled/updated. Try to do this before our
- * triggers are hit */
- for (i = 0; i < reqq.count; i++)
- {
- Id tri = reqq.elements[i];
- FOR_PROVIDES(p2, pp2, tri)
- {
- if (p2 == p)
- continue;
- s2 = pool->solvables + p2;
- if (!s2->repo)
- continue;
- if (s2->name == s->name)
- continue; /* obsoleted anyway */
- if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
- {
- /* deinstall/update p before installing p2 */
-#if 0
- printf("add trigger uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, tri), pool_solvid2str(pool, p));
-#endif
- addedge(od, p2, p, TYPE_CON);
- }
- }
- }
- }
- queue_free(&reqq);
-}
-
-
-/* break an edge in a cycle */
-static void
-breakcycle(struct orderdata *od, Id *cycle)
-{
- Pool *pool = od->trans->pool;
- Id ddegmin, ddegmax, ddeg;
- int k, l;
- struct _TransactionElement *te;
-
- l = 0;
- ddegmin = ddegmax = 0;
- for (k = 0; cycle[k + 1]; k += 2)
- {
- ddeg = od->edgedata[cycle[k + 1] + 1];
- if (ddeg > ddegmax)
- ddegmax = ddeg;
- if (!k || ddeg < ddegmin)
- {
- l = k;
- ddegmin = ddeg;
- continue;
- }
- if (ddeg == ddegmin)
- {
- if (havescripts(pool, od->tes[cycle[l]].p) && !havescripts(pool, od->tes[cycle[k]].p))
- {
- /* prefer k, as l comes from a package with contains scriptlets */
- l = k;
- ddegmin = ddeg;
- continue;
- }
- /* same edge value, check for prereq */
- }
- }
-
- /* record brkoen cycle starting with the tail */
- queue_push(&od->cycles, od->cyclesdata.count); /* offset into data */
- queue_push(&od->cycles, k / 2); /* cycle elements */
- queue_push(&od->cycles, od->edgedata[cycle[l + 1] + 1]); /* broken edge */
- od->ncycles++;
- for (k = l;;)
- {
- k += 2;
- if (!cycle[k + 1])
- k = 0;
- queue_push(&od->cyclesdata, cycle[k]);
- if (k == l)
- break;
- }
- queue_push(&od->cyclesdata, 0); /* mark end */
-
- /* break that edge */
- od->edgedata[cycle[l + 1] + 1] |= TYPE_BROKEN;
-
-#if 1
- if (ddegmin < TYPE_REQ)
- return;
-#endif
-
- /* cycle recorded, print it */
- if (ddegmin >= TYPE_REQ && (ddegmax & TYPE_PREREQ) != 0)
- POOL_DEBUG(SOLV_DEBUG_STATS, "CRITICAL ");
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycle: --> ");
- for (k = 0; cycle[k + 1]; k += 2)
- {
- te = od->tes + cycle[k];
- if ((od->edgedata[cycle[k + 1] + 1] & TYPE_BROKEN) != 0)
- POOL_DEBUG(SOLV_DEBUG_STATS, "%s ##%x##> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
- else
- POOL_DEBUG(SOLV_DEBUG_STATS, "%s --%x--> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "\n");
-}
-
-static inline void
-dump_tes(struct orderdata *od)
-{
- Pool *pool = od->trans->pool;
- int i, j;
- Queue obsq;
- struct _TransactionElement *te, *te2;
-
- queue_init(&obsq);
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- {
- Solvable *s = pool->solvables + te->p;
- POOL_DEBUG(SOLV_DEBUG_RESULT, "TE %4d: %c%s\n", i, s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
- if (s->repo != pool->installed)
- {
- queue_empty(&obsq);
- transaction_all_obs_pkgs(od->trans, te->p, &obsq);
- for (j = 0; j < obsq.count; j++)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " -%s\n", pool_solvid2str(pool, obsq.elements[j]));
- }
- for (j = te->edges; od->edgedata[j]; j += 2)
- {
- te2 = od->tes + od->edgedata[j];
- if ((od->edgedata[j + 1] & TYPE_BROKEN) == 0)
- POOL_DEBUG(SOLV_DEBUG_RESULT, " --%x--> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
- else
- POOL_DEBUG(SOLV_DEBUG_RESULT, " ##%x##> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
- }
- }
-}
-
-#if 1
-static void
-reachable(struct orderdata *od, Id i)
-{
- struct _TransactionElement *te = od->tes + i;
- int j, k;
-
- if (te->mark != 0)
- return;
- te->mark = 1;
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (!od->tes[k].mark)
- reachable(od, k);
- if (od->tes[k].mark == 2)
- {
- te->mark = 2;
- return;
- }
- }
- te->mark = -1;
-}
-#endif
-
-static void
-addcycleedges(struct orderdata *od, Id *cycle, Queue *todo)
-{
-#if 0
- Transaction *trans = od->trans;
- Pool *pool = trans->pool;
-#endif
- struct _TransactionElement *te;
- int i, j, k, tail;
-#if 1
- int head;
-#endif
-
-#if 0
- printf("addcycleedges\n");
- for (i = 0; (j = cycle[i]) != 0; i++)
- printf("cycle %s\n", pool_solvid2str(pool, od->tes[j].p));
-#endif
-
- /* first add all the tail cycle edges */
-
- /* see what we can reach from the cycle */
- queue_empty(todo);
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- te->mark = 0;
- for (i = 0; (j = cycle[i]) != 0; i++)
- {
- od->tes[j].mark = -1;
- queue_push(todo, j);
- }
- while (todo->count)
- {
- i = queue_pop(todo);
- te = od->tes + i;
- if (te->mark > 0)
- continue;
- te->mark = te->mark < 0 ? 2 : 1;
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (od->tes[k].mark > 0)
- continue; /* no need to visit again */
- queue_push(todo, k);
- }
- }
- /* now all cycle TEs are marked with 2, all TEs reachable
- * from the cycle are marked with 1 */
- tail = cycle[0];
- od->tes[tail].mark = 1; /* no need to add edges */
-
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- {
- if (te->mark)
- continue; /* reachable from cycle */
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (od->tes[k].mark != 2)
- continue;
- /* We found an edge to the cycle. Add an extra edge to the tail */
- /* the TE was not reachable, so we're not creating a new cycle! */
-#if 0
- printf("adding TO TAIL cycle edge %d->%d %s->%s!\n", i, tail, pool_solvid2str(pool, od->tes[i].p), pool_solvid2str(pool, od->tes[tail].p));
-#endif
- j -= te->edges; /* in case we move */
- addteedge(od, i, tail, TYPE_CYCLETAIL);
- j += te->edges;
- break; /* one edge is enough */
- }
- }
-
-#if 1
- /* now add all head cycle edges */
-
- /* reset marks */
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- te->mark = 0;
- head = 0;
- for (i = 0; (j = cycle[i]) != 0; i++)
- {
- head = j;
- od->tes[j].mark = 2;
- }
- /* first the head to save some time */
- te = od->tes + head;
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (!od->tes[k].mark)
- reachable(od, k);
- if (od->tes[k].mark == -1)
- od->tes[k].mark = -2; /* no need for another edge */
- }
- for (i = 0; cycle[i] != 0; i++)
- {
- if (cycle[i] == head)
- break;
- te = od->tes + cycle[i];
- for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
- {
- if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- /* see if we can reach a cycle TE from k */
- if (!od->tes[k].mark)
- reachable(od, k);
- if (od->tes[k].mark == -1)
- {
-#if 0
- printf("adding FROM HEAD cycle edge %d->%d %s->%s [%s]!\n", head, k, pool_solvid2str(pool, od->tes[head].p), pool_solvid2str(pool, od->tes[k].p), pool_solvid2str(pool, od->tes[cycle[i]].p));
-#endif
- addteedge(od, head, k, TYPE_CYCLEHEAD);
- od->tes[k].mark = -2; /* no need to add that one again */
- }
- }
- }
-#endif
-}
-
-void
-transaction_order(Transaction *trans, int flags)
-{
- Pool *pool = trans->pool;
- Queue *tr = &trans->steps;
- Repo *installed = pool->installed;
- Id p;
- Solvable *s;
- int i, j, k, numte, numedge;
- struct orderdata od;
- struct _TransactionElement *te;
- Queue todo, obsq, samerepoq, uninstq;
- int cycstart, cycel;
- Id *cycle;
- int oldcount;
- int start, now;
- Repo *lastrepo;
- int lastmedia;
- Id *temedianr;
-
- start = now = solv_timems(0);
- POOL_DEBUG(SOLV_DEBUG_STATS, "ordering transaction\n");
- /* free old data if present */
- if (trans->orderdata)
- {
- struct _TransactionOrderdata *od = trans->orderdata;
- od->tes = solv_free(od->tes);
- od->invedgedata = solv_free(od->invedgedata);
- trans->orderdata = solv_free(trans->orderdata);
- }
-
- /* create a transaction element for every active component */
- numte = 0;
- for (i = 0; i < tr->count; i++)
- {
- p = tr->elements[i];
- s = pool->solvables + p;
- if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
- continue;
- numte++;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "transaction elements: %d\n", numte);
- if (!numte)
- return; /* nothing to do... */
-
- numte++; /* leave first one zero */
- memset(&od, 0, sizeof(od));
- od.trans = trans;
- od.ntes = numte;
- od.tes = solv_calloc(numte, sizeof(*od.tes));
- od.edgedata = solv_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
- od.edgedata[0] = 0;
- od.nedgedata = 1;
- queue_init(&od.cycles);
-
- /* initialize TEs */
- for (i = 0, te = od.tes + 1; i < tr->count; i++)
- {
- p = tr->elements[i];
- s = pool->solvables + p;
- if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
- continue;
- te->p = p;
- te++;
- }
-
- /* create dependency graph */
- for (i = 0; i < tr->count; i++)
- addsolvableedges(&od, pool->solvables + tr->elements[i]);
-
- /* count edges */
- numedge = 0;
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- for (j = te->edges; od.edgedata[j]; j += 2)
- numedge++;
- POOL_DEBUG(SOLV_DEBUG_STATS, "edges: %d, edge space: %d\n", numedge, od.nedgedata / 2);
- POOL_DEBUG(SOLV_DEBUG_STATS, "edge creation took %d ms\n", solv_timems(now));
-
-#if 0
- dump_tes(&od);
-#endif
-
- now = solv_timems(0);
- /* kill all cycles */
- queue_init(&todo);
- for (i = numte - 1; i > 0; i--)
- queue_push(&todo, i);
-
- while (todo.count)
- {
- i = queue_pop(&todo);
- /* printf("- look at TE %d\n", i); */
- if (i < 0)
- {
- i = -i;
- od.tes[i].mark = 2; /* done with that one */
- continue;
- }
- te = od.tes + i;
- if (te->mark == 2)
- continue; /* already finished before */
- if (te->mark == 0)
- {
- int edgestovisit = 0;
- /* new node, visit edges */
- for (j = te->edges; (k = od.edgedata[j]) != 0; j += 2)
- {
- if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- if (od.tes[k].mark == 2)
- continue; /* no need to visit again */
- if (!edgestovisit++)
- queue_push(&todo, -i); /* end of edges marker */
- queue_push(&todo, k);
- }
- if (!edgestovisit)
- te->mark = 2; /* no edges, done with that one */
- else
- te->mark = 1; /* under investigation */
- continue;
- }
- /* oh no, we found a cycle */
- /* find start of cycle node (<0) */
- for (j = todo.count - 1; j >= 0; j--)
- if (todo.elements[j] == -i)
- break;
- assert(j >= 0);
- cycstart = j;
- /* build te/edge chain */
- k = cycstart;
- for (j = k; j < todo.count; j++)
- if (todo.elements[j] < 0)
- todo.elements[k++] = -todo.elements[j];
- cycel = k - cycstart;
- assert(cycel > 1);
- /* make room for edges, two extra element for cycle loop + terminating 0 */
- while (todo.count < cycstart + 2 * cycel + 2)
- queue_push(&todo, 0);
- cycle = todo.elements + cycstart;
- cycle[cycel] = i; /* close the loop */
- cycle[2 * cycel + 1] = 0; /* terminator */
- for (k = cycel; k > 0; k--)
- {
- cycle[k * 2] = cycle[k];
- te = od.tes + cycle[k - 1];
- assert(te->mark == 1);
- te->mark = 0; /* reset investigation marker */
- /* printf("searching for edge from %d to %d\n", cycle[k - 1], cycle[k]); */
- for (j = te->edges; od.edgedata[j]; j += 2)
- if (od.edgedata[j] == cycle[k])
- break;
- assert(od.edgedata[j]);
- cycle[k * 2 - 1] = j;
- }
- /* now cycle looks like this: */
- /* te1 edge te2 edge te3 ... teN edge te1 0 */
- breakcycle(&od, cycle);
- /* restart with start of cycle */
- todo.count = cycstart + 1;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycles broken: %d\n", od.ncycles);
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycle breaking took %d ms\n", solv_timems(now));
-
- now = solv_timems(0);
- /* now go through all broken cycles and create cycle edges to help
- the ordering */
- for (i = od.cycles.count - 3; i >= 0; i -= 3)
- {
- if (od.cycles.elements[i + 2] >= TYPE_REQ)
- addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
- }
- for (i = od.cycles.count - 3; i >= 0; i -= 3)
- {
- if (od.cycles.elements[i + 2] < TYPE_REQ)
- addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "cycle edge creation took %d ms\n", solv_timems(now));
-
-#if 0
- dump_tes(&od);
-#endif
- /* all edges are finally set up and there are no cycles, now the easy part.
- * Create an ordered transaction */
- now = solv_timems(0);
- /* first invert all edges */
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- te->mark = 1; /* term 0 */
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- {
- for (j = te->edges; od.edgedata[j]; j += 2)
- {
- if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- od.tes[od.edgedata[j]].mark++;
- }
- }
- j = 1;
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- {
- te->mark += j;
- j = te->mark;
- }
- POOL_DEBUG(SOLV_DEBUG_STATS, "invedge space: %d\n", j + 1);
- od.invedgedata = solv_calloc(j + 1, sizeof(Id));
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- {
- for (j = te->edges; od.edgedata[j]; j += 2)
- {
- if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
- continue;
- od.invedgedata[--od.tes[od.edgedata[j]].mark] = i;
- }
- }
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- te->edges = te->mark; /* edges now points into invedgedata */
- od.edgedata = solv_free(od.edgedata);
- od.nedgedata = j + 1;
-
- /* now the final ordering */
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- te->mark = 0;
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- for (j = te->edges; od.invedgedata[j]; j++)
- od.tes[od.invedgedata[j]].mark++;
-
- queue_init(&samerepoq);
- queue_init(&uninstq);
- queue_empty(&todo);
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- if (te->mark == 0)
- {
- if (installed && pool->solvables[te->p].repo == installed)
- queue_push(&uninstq, i);
- else
- queue_push(&todo, i);
- }
- assert(todo.count > 0 || uninstq.count > 0);
- oldcount = tr->count;
- queue_empty(tr);
-
- queue_init(&obsq);
-
- lastrepo = 0;
- lastmedia = 0;
- temedianr = solv_calloc(numte, sizeof(Id));
- for (i = 1; i < numte; i++)
- {
- Solvable *s = pool->solvables + od.tes[i].p;
- if (installed && s->repo == installed)
- j = 1;
- else
- j = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
- temedianr[i] = j;
- }
- for (;;)
- {
- /* select an TE i */
- if (uninstq.count)
- i = queue_shift(&uninstq);
- else if (samerepoq.count)
- i = queue_shift(&samerepoq);
- else if (todo.count)
- {
- /* find next repo/media */
- for (j = 0; j < todo.count; j++)
- {
- if (!j || temedianr[todo.elements[j]] < lastmedia)
- {
- i = j;
- lastmedia = temedianr[todo.elements[j]];
- }
- }
- lastrepo = pool->solvables[od.tes[todo.elements[i]].p].repo;
-
- /* move all matching TEs to samerepoq */
- for (i = j = 0; j < todo.count; j++)
- {
- int k = todo.elements[j];
- if (temedianr[k] == lastmedia && pool->solvables[od.tes[k].p].repo == lastrepo)
- queue_push(&samerepoq, k);
- else
- todo.elements[i++] = k;
- }
- todo.count = i;
-
- assert(samerepoq.count);
- i = queue_shift(&samerepoq);
- }
- else
- break;
-
- te = od.tes + i;
- queue_push(tr, te->p);
-#if 0
-printf("do %s [%d]\n", pool_solvid2str(pool, te->p), temedianr[i]);
-#endif
- s = pool->solvables + te->p;
- for (j = te->edges; od.invedgedata[j]; j++)
- {
- struct _TransactionElement *te2 = od.tes + od.invedgedata[j];
- assert(te2->mark > 0);
- if (--te2->mark == 0)
- {
- Solvable *s = pool->solvables + te2->p;
-#if 0
-printf("free %s [%d]\n", pool_solvid2str(pool, te2->p), temedianr[od.invedgedata[j]]);
-#endif
- if (installed && s->repo == installed)
- queue_push(&uninstq, od.invedgedata[j]);
- else if (s->repo == lastrepo && temedianr[od.invedgedata[j]] == lastmedia)
- queue_push(&samerepoq, od.invedgedata[j]);
- else
- queue_push(&todo, od.invedgedata[j]);
- }
- }
- }
- solv_free(temedianr);
- queue_free(&todo);
- queue_free(&samerepoq);
- queue_free(&uninstq);
- queue_free(&obsq);
- for (i = 1, te = od.tes + i; i < numte; i++, te++)
- assert(te->mark == 0);
-
- /* add back obsoleted packages */
- transaction_add_obsoleted(trans);
- assert(tr->count == oldcount);
-
- POOL_DEBUG(SOLV_DEBUG_STATS, "creating new transaction took %d ms\n", solv_timems(now));
- POOL_DEBUG(SOLV_DEBUG_STATS, "transaction ordering took %d ms\n", solv_timems(start));
-
- if ((flags & SOLVER_TRANSACTION_KEEP_ORDERDATA) != 0)
- {
- trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
- trans->orderdata->tes = od.tes;
- trans->orderdata->ntes = numte;
- trans->orderdata->invedgedata = od.invedgedata;
- trans->orderdata->ninvedgedata = od.nedgedata;
- }
- else
- {
- solv_free(od.tes);
- solv_free(od.invedgedata);
- }
- queue_free(&od.cycles);
- queue_free(&od.cyclesdata);
-}
-
-
-int
-transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices)
-{
- int i, j;
- struct _TransactionOrderdata *od = trans->orderdata;
- struct _TransactionElement *te;
-
- if (!od)
- return choices->count;
- if (!chosen)
- {
- /* initialization step */
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- te->mark = 0;
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- {
- for (j = te->edges; od->invedgedata[j]; j++)
- od->tes[od->invedgedata[j]].mark++;
- }
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (!te->mark)
- queue_push(choices, te->p);
- return choices->count;
- }
- for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
- if (te->p == chosen)
- break;
- if (i == od->ntes)
- return choices->count;
- if (te->mark > 0)
- {
- /* hey! out-of-order installation! */
- te->mark = -1;
- }
- for (j = te->edges; od->invedgedata[j]; j++)
- {
- te = od->tes + od->invedgedata[j];
- assert(te->mark > 0 || te->mark == -1);
- if (te->mark > 0 && --te->mark == 0)
- queue_push(choices, te->p);
- }
- return choices->count;
-}
-
-void
-transaction_add_obsoleted(Transaction *trans)
-{
- Pool *pool = trans->pool;
- Repo *installed = pool->installed;
- Id p;
- Solvable *s;
- int i, j, k, max;
- Map done;
- Queue obsq, *steps;
-
- if (!installed || !trans->steps.count)
- return;
- /* calculate upper bound */
- max = 0;
- FOR_REPO_SOLVABLES(installed, p, s)
- if (MAPTST(&trans->transactsmap, p))
- max++;
- if (!max)
- return;
- /* make room */
- steps = &trans->steps;
- queue_insertn(steps, 0, max, 0);
-
- /* now add em */
- map_init(&done, installed->end - installed->start);
- queue_init(&obsq);
- for (j = 0, i = max; i < steps->count; i++)
- {
- p = trans->steps.elements[i];
- if (pool->solvables[p].repo == installed)
- {
- if (!trans->transaction_installed[p - pool->installed->start])
- trans->steps.elements[j++] = p;
- continue;
- }
- trans->steps.elements[j++] = p;
- queue_empty(&obsq);
- transaction_all_obs_pkgs(trans, p, &obsq);
- for (k = 0; k < obsq.count; k++)
- {
- p = obsq.elements[k];
- assert(p >= installed->start && p < installed->end);
- if (!MAPTST(&trans->transactsmap, p)) /* just in case */
- continue;
- if (MAPTST(&done, p - installed->start))
- continue;
- MAPSET(&done, p - installed->start);
- trans->steps.elements[j++] = p;
- }
- }
-
- /* free unneeded space */
- queue_truncate(steps, j);
- map_free(&done);
- queue_free(&obsq);
-}
-
-static void
-transaction_check_pkg(Transaction *trans, Id tepkg, Id pkg, Map *ins, Map *seen, int onlyprereq, Id noconfpkg, int depth)
-{
- Pool *pool = trans->pool;
- Id p, pp;
- Solvable *s;
- int good;
-
- if (MAPTST(seen, pkg))
- return;
- MAPSET(seen, pkg);
- s = pool->solvables + pkg;
-#if 0
- printf("- %*s%c%s\n", depth * 2, "", s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
-#endif
- if (s->requires)
- {
- Id req, *reqp;
- int inpre = 0;
- reqp = s->repo->idarraydata + s->requires;
- while ((req = *reqp++) != 0)
- {
- if (req == SOLVABLE_PREREQMARKER)
- {
- inpre = 1;
- continue;
- }
- if (onlyprereq && !inpre)
- continue;
- if (!strncmp(pool_id2str(pool, req), "rpmlib(", 7))
- continue;
- good = 0;
- /* first check kept packages, then freshly installed, then not yet uninstalled */
- FOR_PROVIDES(p, pp, req)
- {
- if (!MAPTST(ins, p))
- continue;
- if (MAPTST(&trans->transactsmap, p))
- continue;
- good++;
- transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
- }
- if (!good)
- {
- FOR_PROVIDES(p, pp, req)
- {
- if (!MAPTST(ins, p))
- continue;
- if (pool->solvables[p].repo == pool->installed)
- continue;
- good++;
- transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
- }
- }
- if (!good)
- {
- FOR_PROVIDES(p, pp, req)
- {
- if (!MAPTST(ins, p))
- continue;
- good++;
- transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
- }
- }
- if (!good)
- {
- POOL_DEBUG(SOLV_DEBUG_RESULT, " %c%s: nothing provides %s needed by %c%s\n", pool->solvables[tepkg].repo == pool->installed ? '-' : '+', pool_solvid2str(pool, tepkg), pool_dep2str(pool, req), s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
- }
- }
- }
+ transaction_free_orderdata(trans);
+ free(trans);
}
-void
-transaction_check_order(Transaction *trans)
-{
- Pool *pool = trans->pool;
- Solvable *s;
- Id p, lastins;
- Map ins, seen;
- int i;
-
- POOL_DEBUG(SOLV_DEBUG_RESULT, "\nchecking transaction order...\n");
- map_init(&ins, pool->nsolvables);
- map_init(&seen, pool->nsolvables);
- if (pool->installed)
- FOR_REPO_SOLVABLES(pool->installed, p, s)
- MAPSET(&ins, p);
- lastins = 0;
- for (i = 0; i < trans->steps.count; i++)
- {
- p = trans->steps.elements[i];
- s = pool->solvables + p;
- if (s->repo != pool->installed)
- lastins = p;
- if (s->repo != pool->installed)
- MAPSET(&ins, p);
- if (havescripts(pool, p))
- {
- MAPZERO(&seen);
- transaction_check_pkg(trans, p, p, &ins, &seen, 1, lastins, 0);
- }
- if (s->repo == pool->installed)
- MAPCLR(&ins, p);
- }
- map_free(&seen);
- map_free(&ins);
- POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction order check done.\n");
-}
diff --git a/src/transaction.h b/src/transaction.h
index 36b0c6f..c840838 100644
--- a/src/transaction.h
+++ b/src/transaction.h
@@ -85,12 +85,17 @@ typedef struct _Transaction {
/* order flags */
#define SOLVER_TRANSACTION_KEEP_ORDERDATA (1 << 0)
+#define SOLVER_TRANSACTION_KEEP_ORDERCYCLES (1 << 1)
+
+/* cycle severities */
+#define SOLVER_ORDERCYCLE_HARMLESS 0
+#define SOLVER_ORDERCYCLE_NORMAL 1
+#define SOLVER_ORDERCYCLE_CRITICAL 2
extern Transaction *transaction_create(struct _Pool *pool);
extern Transaction *transaction_create_decisionq(struct _Pool *pool, Queue *decisionq, Map *multiversionmap);
extern Transaction *transaction_create_clone(Transaction *srctrans);
extern void transaction_free(Transaction *trans);
-extern void transaction_free_orderdata(Transaction *trans);
/* if p is installed, returns with pkg(s) obsolete p */
/* if p is not installed, returns with pkg(s) we obsolete */
@@ -113,6 +118,8 @@ extern int transaction_installedresult(Transaction *trans, Queue *installedq);
int transaction_calc_installsizechange(Transaction *trans);
void transaction_calc_duchanges(Transaction *trans, struct _DUChanges *mps, int nmps);
+
+
/* order a transaction */
extern void transaction_order(Transaction *trans, int flags);
@@ -127,6 +134,12 @@ extern void transaction_add_obsoleted(Transaction *trans);
/* debug function, report problems found in the order */
extern void transaction_check_order(Transaction *trans);
+/* order cycle introspection */
+extern void transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity);
+extern int transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q);
+
+extern void transaction_free_orderdata(Transaction *trans);
+extern void transaction_clone_orderdata(Transaction *trans, Transaction *srctrans);
#ifdef __cplusplus
}
diff --git a/tools/installcheck.c b/tools/installcheck.c
index 3ef67b0..e8be171 100644
--- a/tools/installcheck.c
+++ b/tools/installcheck.c
@@ -2,7 +2,7 @@
*/
/*
- * Copyright (c) 2009, Novell Inc.
+ * Copyright (c) 2009-2015, SUSE LLC
*
* This program is licensed under the BSD license, read LICENSE.BSD
* for further information
@@ -49,6 +49,20 @@ usage(char** argv)
exit(1);
}
+static int
+strlen_comp(const char *str)
+{
+ size_t l = strlen(str);
+ if (l > 3 && !strcmp(str + l - 3, ".gz"))
+ return l - 3;
+ if (l > 3 && !strcmp(str + l - 3, ".xz"))
+ return l - 3;
+ if (l > 4 && !strcmp(str + l - 4, ".bz2"))
+ return l - 4;
+ if (l > 5 && !strcmp(str + l - 4, ".lzma"))
+ return l - 5;
+ return l;
+}
int
main(int argc, char **argv)
@@ -112,7 +126,7 @@ main(int argc, char **argv)
++i;
continue;
}
- l = strlen(argv[i]);
+ l = strlen_comp(argv[i]);
if (!strcmp(argv[i], "-"))
fp = stdin;
else if ((fp = solv_xfopen(argv[i], 0)) == 0)
@@ -126,33 +140,40 @@ main(int argc, char **argv)
{
}
#ifdef ENABLE_SUSEREPO
- else if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
- {
- r = repo_add_susetags(repo, fp, 0, 0, 0);
- }
- else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
+ else if (l >= 8 && !strncmp(argv[i] + l - 8, "packages", 8))
{
r = repo_add_susetags(repo, fp, 0, 0, 0);
}
#endif
#ifdef ENABLE_RPMMD
- else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
+ else if (l >= 11 && !strncmp(argv[i] + l - 11, "primary.xml", 11))
{
r = repo_add_rpmmd(repo, fp, 0, 0);
+ if (!r && i + 1 < argc)
+ {
+ l = strlen_comp(argv[i + 1]);
+ if (l >= 13 && !strncmp(argv[i + 1] + l - 13, "filelists.xml", 13))
+ {
+ i++;
+ fclose(fp);
+ if ((fp = solv_xfopen(argv[i], 0)) == 0)
+ {
+ perror(argv[i]);
+ exit(1);
+ }
+ r = repo_add_rpmmd(repo, fp, 0, REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+ }
+ }
}
#endif
#ifdef ENABLE_DEBIAN
- else if (l >= 8 && !strcmp(argv[i] + l - 8, "Packages"))
- {
- r = repo_add_debpackages(repo, fp, 0);
- }
- else if (l >= 11 && !strcmp(argv[i] + l - 11, "Packages.gz"))
+ else if (l >= 8 && !strncmp(argv[i] + l - 8, "Packages", 8))
{
r = repo_add_debpackages(repo, fp, 0);
}
#endif
#ifdef ENABLE_ARCHREPO
- else if (l >= 10 && (!strcmp(argv[i] + l - 10, ".db.tar.gz") || !strcmp(argv[i] + l - 10, ".db.tar.xz")))
+ else if (l >= 7 && (!strncmp(argv[i] + l - 7, ".db.tar", 7)))
{
r = repo_add_arch_repo(repo, fp, 0);
}
@@ -281,14 +302,6 @@ main(int argc, char **argv)
cand.elements[j++] = p;
continue;
}
-#if 0
- Solvable *s = pool->solvables + p;
- if (!strcmp(pool_id2str(pool, s->name), "libusb-compat-devel"))
- {
- cand.elements[j++] = p;
- continue;
- }
-#endif
}
cand.count = j;
if (i == j)
@@ -422,15 +435,6 @@ main(int argc, char **argv)
}
}
}
-#if 0
- else
- {
- if (!strcmp(pool_id2str(pool, s->name), "libusb-compat-devel"))
- {
- solver_printdecisions(solv);
- }
- }
-#endif
}
solver_free(solv);
exit(status);