From 70c6207bfdb27f170377015f65f22e8ba50f6a10 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 27 Oct 2016 14:54:40 +0900 Subject: [PATCH] Imported Upstream version 0.6.13 Change-Id: I9fed388d0b8658275d9089b22473596379566069 Signed-off-by: DongHun Kwak --- CMakeLists.txt | 11 +- VERSION.cmake | 2 +- bindings/solv.i | 1 + doc/filters/xcode.pl | 2 +- doc/libsolv-bindings.3 | 13 +- doc/libsolv-bindings.txt | 5 + examples/CMakeLists.txt | 11 +- examples/solv.c | 3415 -------------------------------- examples/solv/CMakeLists.txt | 29 + examples/solv/checksig.c | 126 ++ examples/solv/checksig.h | 3 + examples/solv/deltarpm.c | 133 ++ examples/solv/deltarpm.h | 1 + examples/solv/fastestmirror.c | 149 ++ examples/solv/fastestmirror.h | 2 + examples/solv/fileconflicts.c | 76 + examples/solv/fileconflicts.h | 2 + examples/solv/fileprovides.c | 94 + examples/solv/fileprovides.h | 1 + examples/solv/mirror.c | 110 + examples/solv/mirror.h | 2 + examples/solv/patchjobs.c | 63 + examples/solv/patchjobs.h | 2 + examples/solv/repoinfo.c | 275 +++ examples/solv/repoinfo.h | 53 + examples/solv/repoinfo_cache.c | 290 +++ examples/solv/repoinfo_cache.h | 12 + examples/solv/repoinfo_config_debian.c | 127 ++ examples/solv/repoinfo_config_debian.h | 1 + examples/solv/repoinfo_config_urpmi.c | 106 + examples/solv/repoinfo_config_urpmi.h | 2 + examples/solv/repoinfo_config_yum.c | 233 +++ examples/solv/repoinfo_config_yum.h | 2 + examples/solv/repoinfo_download.c | 221 +++ examples/solv/repoinfo_download.h | 7 + examples/solv/repoinfo_system_debian.c | 108 + examples/solv/repoinfo_system_debian.h | 2 + examples/solv/repoinfo_system_rpm.c | 151 ++ examples/solv/repoinfo_system_rpm.h | 2 + examples/solv/repoinfo_type_debian.c | 202 ++ examples/solv/repoinfo_type_debian.h | 2 + examples/solv/repoinfo_type_mdk.c | 213 ++ examples/solv/repoinfo_type_mdk.h | 3 + examples/solv/repoinfo_type_rpmmd.c | 207 ++ examples/solv/repoinfo_type_rpmmd.h | 2 + examples/solv/repoinfo_type_susetags.c | 274 +++ examples/solv/repoinfo_type_susetags.h | 2 + examples/solv/solv.c | 910 +++++++++ ext/repo_autopattern.c | 9 +- ext/repo_mdk.c | 2 + ext/testcase.c | 25 +- package/libsolv.changes | 13 +- src/CMakeLists.txt | 2 +- src/hash.h | 4 +- src/libsolv.ver | 2 + src/order.c | 1345 +++++++++++++ src/solver.c | 80 +- src/solver.h | 7 +- src/transaction.c | 1244 +----------- src/transaction.h | 15 +- tools/installcheck.c | 66 +- 61 files changed, 5739 insertions(+), 4735 deletions(-) delete mode 100644 examples/solv.c create mode 100644 examples/solv/CMakeLists.txt create mode 100644 examples/solv/checksig.c create mode 100644 examples/solv/checksig.h create mode 100644 examples/solv/deltarpm.c create mode 100644 examples/solv/deltarpm.h create mode 100644 examples/solv/fastestmirror.c create mode 100644 examples/solv/fastestmirror.h create mode 100644 examples/solv/fileconflicts.c create mode 100644 examples/solv/fileconflicts.h create mode 100644 examples/solv/fileprovides.c create mode 100644 examples/solv/fileprovides.h create mode 100644 examples/solv/mirror.c create mode 100644 examples/solv/mirror.h create mode 100644 examples/solv/patchjobs.c create mode 100644 examples/solv/patchjobs.h create mode 100644 examples/solv/repoinfo.c create mode 100644 examples/solv/repoinfo.h create mode 100644 examples/solv/repoinfo_cache.c create mode 100644 examples/solv/repoinfo_cache.h create mode 100644 examples/solv/repoinfo_config_debian.c create mode 100644 examples/solv/repoinfo_config_debian.h create mode 100644 examples/solv/repoinfo_config_urpmi.c create mode 100644 examples/solv/repoinfo_config_urpmi.h create mode 100644 examples/solv/repoinfo_config_yum.c create mode 100644 examples/solv/repoinfo_config_yum.h create mode 100644 examples/solv/repoinfo_download.c create mode 100644 examples/solv/repoinfo_download.h create mode 100644 examples/solv/repoinfo_system_debian.c create mode 100644 examples/solv/repoinfo_system_debian.h create mode 100644 examples/solv/repoinfo_system_rpm.c create mode 100644 examples/solv/repoinfo_system_rpm.h create mode 100644 examples/solv/repoinfo_type_debian.c create mode 100644 examples/solv/repoinfo_type_debian.h create mode 100644 examples/solv/repoinfo_type_mdk.c create mode 100644 examples/solv/repoinfo_type_mdk.h create mode 100644 examples/solv/repoinfo_type_rpmmd.c create mode 100644 examples/solv/repoinfo_type_rpmmd.h create mode 100644 examples/solv/repoinfo_type_susetags.c create mode 100644 examples/solv/repoinfo_type_susetags.h create mode 100644 examples/solv/solv.c create mode 100644 src/order.c 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() { $_ = " $_"; $_ = "$_ "; if (s/^ TCL +/ /) { - s/(\$[a-zA-Z_][a-zA-Z0-9_]*)/<-S>$1<-I>/g; + s/(\$[a-zA-Z_][a-zA-Z0-9_:]*)/<-S>$1<-I>/g; } else { s/(?<=[^a-zA-Z_\&:\.\'\";])(?!solv\W|Solv\W|Pool\W)([\$\@a-zA-Z_][a-zA-Z0-9_]*)(?=[^a-zA-Z0-9_\(;\[])(?!::)(?! [^=])/<-S>$1<-I>/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 -.\" 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#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, "", 20)) - { - bp += 20; - if (solv_hex2bin((const char **)&bp, chksump, 32) == 32) - *chksumtypep = REPOKEY_TYPE_SHA256; - continue; - } - if (strncmp(bp, "'); - if (!bp) - continue; - bp++; - ep = strstr(bp, "repodata/repomd.xml"); - 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 \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 +#include +#include +#include +#include + +#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); -- 2.7.4