/* $Id: impact.c,v 1.27 2009/05/07 21:22:52 imil Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Emile "iMil" Heitor . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* pkg_impact() calculates "impact" of installing a package * it is the heart of package install / update */ /* pkg_impact rationale * * pkg_impact() receive a package list as an argument. * Those are the packages to be installed / upgraded. * * For every package, a dependency tree is loaded, * and every dependency is passed to deps_impact() * deps_impact() loops through local (installed) packages * and matches depencendy over them. * If the dependency exists but its version does not * satisfy pkg_match(), the package is marked as * "to upgrade", meaning it will be deleted (oldpkg) and * a new version will be installed (pkgname). * * Finally, the package itself is passed to deps_impact() * to figure out if it needs to be installed or upgraded */ #include "pkg_dry.h" #include "dewey.h" /* free impact list */ void free_impact(Impacthead *impacthead) { Pkgimpact *pimpact; if (impacthead == NULL) return; while (!SLIST_EMPTY(impacthead)) { pimpact = SLIST_FIRST(impacthead); SLIST_REMOVE_HEAD(impacthead, next); XFREE(pimpact->depname); XFREE(pimpact->oldpkg); XFREE(pimpact->pkgname); XFREE(pimpact); } } /* check wether or not a dependency is already recorded in the impact list */ static int dep_present(Impacthead *impacthead, char *depname) { Pkgimpact *pimpact; SLIST_FOREACH(pimpact, impacthead, next) if (pimpact->pkgname != NULL && pkg_match(depname, pimpact->pkgname)) return 1; return 0; } /* return a pkgname corresponding to a dependency */ Pkglist * map_pkg_to_dep(Plisthead *plisthead, char *depname) { Pkglist *plist; SLIST_FOREACH(plist, plisthead, next) if (pkg_match(depname, plist->pkgname)) return plist; return NULL; } /* similar to opattern.c's pkg_order but without pattern */ static int version_check(char *first_pkg, char *second_pkg) { char *first_ver, *second_ver; first_ver = strrchr(first_pkg, '-'); second_ver = strrchr(second_pkg, '-'); if (first_ver == NULL) return 2; if (second_ver == NULL) return 1; if (dewey_cmp(first_ver + 1, DEWEY_GT, second_ver + 1)) return 1; else return 2; } /* loop through local packages and match for upgrades */ static int deps_impact(Impacthead *impacthead, Plisthead *localplisthead, Plisthead *remoteplisthead, Pkgdeptree *pdp) { int matchlen, toupgrade; Pkgimpact *pimpact; Pkglist *plist, *mapplist; char *localmatch, *remotepkg, *matchname; /* build match name */ matchlen = strlen(pdp->matchname) + 2; XMALLOC(matchname, matchlen * sizeof(char)); snprintf(matchname, matchlen, "%s%c", pdp->matchname, DELIMITER); /* record corresponding package on remote list*/ mapplist = map_pkg_to_dep(remoteplisthead, pdp->depname); XSTRDUP(remotepkg, mapplist->pkgname); /* create initial impact entry with a DONOTHING status, permitting * to check if this dependency has already been recorded */ XMALLOC(pimpact, sizeof(Pkgimpact)); XSTRDUP(pimpact->depname, pdp->depname); pimpact->action = DONOTHING; pimpact->oldpkg = NULL; pimpact->pkgname = NULL; SLIST_INSERT_HEAD(impacthead, pimpact, next); /* parse local packages to see if depedency is installed*/ SLIST_FOREACH(plist, localplisthead, next) { localmatch = end_expr(plist->pkgname); /* foo| */ /* match, package is installed */ if (strncmp(localmatch, matchname, strlen(localmatch)) == 0) { /* default action when local package match */ toupgrade = TOUPGRADE; /* installed version does not match dep requirement */ if (!pkg_match(pdp->depname, plist->pkgname)) { /* local pkgname didn't match deps, remote pkg has a * lesser version than local package. */ if (version_check(plist->pkgname, remotepkg) == 1) { printf("warning: installed package %s has a greater version than %s (to be installed)\n", plist->pkgname, remotepkg); if (!check_yesno()) toupgrade = DONOTHING; } /* insert as an upgrade */ /* oldpkg is used when building removal order list */ XSTRDUP(pimpact->oldpkg, plist->pkgname); pimpact->action = toupgrade; pimpact->pkgname = remotepkg; /* record package dependency deepness */ pimpact->level = pdp->level; /* record binary package size */ pimpact->file_size = plist->file_size; /* record installed package size */ pimpact->size_pkg = plist->size_pkg; } XFREE(localmatch); XFREE(matchname); return 1; } /* if installed package match */ XFREE(localmatch); } /* SLIST_FOREACH plist */ if (!dep_present(impacthead, pdp->matchname)) { pimpact->oldpkg = NULL; pimpact->action = TOINSTALL; pimpact->pkgname = remotepkg; /* record package dependency deepness */ pimpact->level = pdp->level; pimpact->file_size = mapplist->file_size; pimpact->size_pkg = mapplist->size_pkg; } XFREE(matchname); return 1; } /* count if there's many packages with the same basename */ int count_samepkg(Plisthead *plisthead, const char *pkgname) { Pkglist *pkglist; char *plistpkg = NULL, **samepkg = NULL; int count = 0, num = 0, pkglen; /* count if there's many packages with this name */ SLIST_FOREACH(pkglist, plisthead, next) { XSTRDUP(plistpkg, pkglist->pkgname); pkglen = strlen(pkgname); if (strlen(plistpkg) < (pkglen + 1)) { XFREE(plistpkg); continue; } if (plistpkg[pkglen] != '-' && !isdigit((int)plistpkg[pkglen + 1])) { XFREE(plistpkg); continue; } trunc_str(plistpkg, '-', STR_BACKWARD); pkglen = max(strlen(pkgname), strlen(plistpkg)); if (strncmp(pkgname, plistpkg, pkglen) == 0) { XREALLOC(samepkg, (count + 2) * sizeof(char *)); XSTRDUP(samepkg[count], pkglist->pkgname); samepkg[count + 1] = NULL; count++; } XFREE(plistpkg); } if (count > 1) { /* there was more than one reference */ printf("there's more than one version available for this package.\n"); printf("please re-run %s with a package name matching one of the following:\n", getprogname()); for (num = 0; num < count; num++) printf("%s\n", samepkg[num]); } free_list(samepkg); return count; } Impacthead * pkg_impact(char **pkgargs) { Plisthead *localplisthead; Plisthead *remoteplisthead; Deptreehead pdphead; Impacthead *impacthead; Pkgimpact *pimpact, *tmpimpact; Pkgdeptree *pdp; Pkglist *plist; char **ppkgargs, *pkgname = NULL; int exists; /* record local package list */ localplisthead = rec_pkglist(LOCAL_PKGS_QUERY); /* record remote package list */ remoteplisthead = rec_pkglist(REMOTE_PKGS_QUERY); if (remoteplisthead == NULL) { printf("empty available package list.\n"); return NULL; } SLIST_INIT(&pdphead); XMALLOC(impacthead, sizeof(Impacthead)); SLIST_INIT(impacthead); /* retreive impact list for all packages listed in the command line */ for (ppkgargs = pkgargs; *ppkgargs != NULL; ppkgargs++) { if (strpbrk(*ppkgargs, "*%") != NULL) /* avoid SQL jokers */ continue; /* check if this is a multiple-version package (apache, ...) */ if (count_samepkg(remoteplisthead, *ppkgargs) > 1) goto impactend; else /* dependencies discovery */ full_dep_tree(*ppkgargs, DIRECT_DEPS, &pdphead, __func__, 0); /* parse dependencies for pkgname */ SLIST_FOREACH(pdp, &pdphead, next) { exists = 0; /* dependency is already present in impact list */ SLIST_FOREACH(pimpact, impacthead, next) { if (strncmp(pimpact->depname, pdp->depname, strlen(pdp->depname)) == 0) exists = 1; } if (exists) continue; /* compare needed deps with local packages */ if (!deps_impact(impacthead, localplisthead, remoteplisthead, pdp)) { /* there was a versionning mismatch, proceed ? */ if (!check_yesno()) { free_impact(impacthead); XFREE(impacthead); impacthead = NULL; goto impactend; /* avoid free's repetition */ } } } /* SLIST_FOREACH deps */ /* finally, insert package itself */ SLIST_FOREACH(plist, remoteplisthead, next) { /* find corresponding pkg */ XSTRDUP(pkgname, plist->pkgname); if (!exact_pkgfmt(*ppkgargs)) /* argument is not a full pkg name, truncate plist->pkgname */ trunc_str(pkgname, '-', STR_BACKWARD); if (strncmp(pkgname, *ppkgargs, strlen(pkgname)) == 0) { /* use a Pkgdeptree for convenience */ XMALLOC(pdp, sizeof(Pkgdeptree)); XSTRDUP(pdp->matchname, plist->pkgname); trunc_str(pdp->matchname, '-', STR_BACKWARD); /* passing pkgname as depname */ XSTRDUP(pdp->depname, plist->pkgname); deps_impact(impacthead, localplisthead, remoteplisthead, pdp); XFREE(pdp->depname); XFREE(pdp->matchname); XFREE(pdp); XFREE(pkgname); break; } XFREE(pkgname); } /* SLIST_FOREACH remoteplisthead */ } /* for (ppkgargs) */ impactend: free_deptree(&pdphead); free_pkglist(localplisthead); free_pkglist(remoteplisthead); /* remove DONOTHING entries */ SLIST_FOREACH(pimpact, impacthead, next) { if (pimpact->action == DONOTHING) { tmpimpact = pimpact; SLIST_REMOVE(impacthead, pimpact, Pkgimpact, next); XFREE(tmpimpact->depname); XFREE(tmpimpact->pkgname); XFREE(tmpimpact->oldpkg); XFREE(tmpimpact); } } return impacthead; }