/* * sync.c * * Copyright (c) 2002-2006 by Judd Vinet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "config.h" #include #include #include #include #ifdef CYGWIN #include /* PATH_MAX */ #endif #include #include /* pacman */ #include "log.h" #include "util.h" #include "error.h" #include "list.h" #include "package.h" #include "db.h" #include "cache.h" #include "deps.h" #include "conflict.h" #include "provide.h" #include "trans.h" #include "sync.h" #include "versioncmp.h" #include "handle.h" #include "alpm.h" extern pmhandle_t *handle; pmsyncpkg_t *_alpm_sync_new(int type, pmpkg_t *spkg, void *data) { pmsyncpkg_t *sync; if((sync = (pmsyncpkg_t *)malloc(sizeof(pmsyncpkg_t))) == NULL) { return(NULL); } sync->type = type; sync->pkg = spkg; sync->data = data; return(sync); } void _alpm_sync_free(void *data) { pmsyncpkg_t *sync = data; if(sync == NULL) { return; } if(sync->type == PM_SYNC_TYPE_REPLACE) { FREELISTPKGS(sync->data); } else { FREEPKG(sync->data); } free(sync); } /* Test for existence of a package in a PMList* of pmsyncpkg_t* * If found, return a pointer to the respective pmsyncpkg_t* */ static pmsyncpkg_t *find_pkginsync(char *needle, PMList *haystack) { PMList *i; pmsyncpkg_t *sync = NULL; int found = 0; for(i = haystack; i && !found; i = i->next) { sync = i->data; if(sync && !strcmp(sync->pkg->name, needle)) { found = 1; } } if(!found) { sync = NULL; } return(sync); } /* It returns a PMList of packages extracted from the given archive * (the archive must have been generated by gensync) */ PMList *_alpm_sync_load_dbarchive(char *archive) { PMList *lp = NULL; DIR *dir = NULL; TAR *tar = NULL; tartype_t gztype = { (openfunc_t)_alpm_gzopen_frontend, (closefunc_t)gzclose, (readfunc_t)gzread, (writefunc_t)gzwrite }; if(tar_open(&tar, archive, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { pm_errno = PM_ERR_NOT_A_FILE; goto error; } /* ORE - readdir tmp_dir */ /* for each subdir, parse %s/desc and %s/depends */ /* then db_write it */ tar_close(tar); return(lp); error: if(tar) { tar_close(tar); } if(dir) { closedir(dir); } return(NULL); } int _alpm_sync_sysupgrade(pmtrans_t *trans, pmdb_t *db_local, PMList *dbs_sync) { PMList *i, *j, *k; /* check for "recommended" package replacements */ _alpm_log(PM_LOG_FLOW1, "checking for package replacements"); for(i = dbs_sync; i; i = i->next) { for(j = _alpm_db_get_pkgcache(i->data); j; j = j->next) { pmpkg_t *spkg = j->data; for(k = spkg->replaces; k; k = k->next) { PMList *m; for(m = _alpm_db_get_pkgcache(db_local); m; m = m->next) { pmpkg_t *lpkg = m->data; if(!strcmp(k->data, lpkg->name)) { _alpm_log(PM_LOG_DEBUG, "checking replacement '%s' for package '%s'", k->data, spkg->name); if(_alpm_list_is_strin(lpkg->name, handle->ignorepkg)) { _alpm_log(PM_LOG_WARNING, "%s-%s: ignoring package upgrade (to be replaced by %s-%s)", lpkg->name, lpkg->version, spkg->name, spkg->version); } else { /* get confirmation for the replacement */ int doreplace = 0; QUESTION(trans, PM_TRANS_CONV_REPLACE_PKG, lpkg, spkg, ((pmdb_t *)i->data)->treename, &doreplace); if(doreplace) { /* if confirmed, add this to the 'final' list, designating 'lpkg' as * the package to replace. */ pmsyncpkg_t *sync; pmpkg_t *dummy = _alpm_pkg_new(lpkg->name, NULL); if(dummy == NULL) { pm_errno = PM_ERR_MEMORY; goto error; } dummy->requiredby = _alpm_list_strdup(lpkg->requiredby); /* check if spkg->name is already in the packages list. */ sync = find_pkginsync(spkg->name, trans->packages); if(sync) { /* found it -- just append to the replaces list */ sync->data = _alpm_list_add(sync->data, dummy); } else { /* none found -- enter pkg into the final sync list */ sync = _alpm_sync_new(PM_SYNC_TYPE_REPLACE, spkg, NULL); if(sync == NULL) { FREEPKG(dummy); pm_errno = PM_ERR_MEMORY; goto error; } sync->data = _alpm_list_add(NULL, dummy); trans->packages = _alpm_list_add(trans->packages, sync); } _alpm_log(PM_LOG_FLOW2, "%s-%s elected for upgrade (to be replaced by %s-%s)", lpkg->name, lpkg->version, spkg->name, spkg->version); } } break; } } } } } /* match installed packages with the sync dbs and compare versions */ _alpm_log(PM_LOG_FLOW1, "checking for package upgrades"); for(i = _alpm_db_get_pkgcache(db_local); i; i = i->next) { int cmp; int replace = 0; pmpkg_t *local = i->data; pmpkg_t *spkg = NULL; pmsyncpkg_t *sync; for(j = dbs_sync; !spkg && j; j = j->next) { spkg = _alpm_db_get_pkgfromcache(j->data, local->name); } if(spkg == NULL) { _alpm_log(PM_LOG_DEBUG, "'%s' not found in sync db -- skipping", local->name); continue; } /* we don't care about a to-be-replaced package's newer version */ for(j = trans->packages; j && !replace; j = j->next) { sync = j->data; if(sync->type == PM_SYNC_TYPE_REPLACE) { if(_alpm_pkg_isin(spkg->name, sync->data)) { replace = 1; } } } if(replace) { _alpm_log(PM_LOG_DEBUG, "'%s' is already elected for removal -- skipping", spkg->name); continue; } /* compare versions and see if we need to upgrade */ cmp = _alpm_versioncmp(local->version, spkg->version); if(cmp > 0 && !spkg->force) { /* local version is newer */ _alpm_log(PM_LOG_WARNING, "%s-%s: local version is newer", local->name, local->version); } else if(cmp == 0) { /* versions are identical */ } else if(_alpm_list_is_strin(i->data, handle->ignorepkg)) { /* package should be ignored (IgnorePkg) */ _alpm_log(PM_LOG_WARNING, "%s-%s: ignoring package upgrade (%s)", local->name, local->version, spkg->version); } else { _alpm_log(PM_LOG_FLOW2, "%s-%s elected for upgrade (%s => %s)", local->name, local->version, local->version, spkg->version); if(!find_pkginsync(spkg->name, trans->packages)) { pmpkg_t *dummy = _alpm_pkg_new(local->name, local->version); sync = _alpm_sync_new(PM_SYNC_TYPE_UPGRADE, spkg, dummy); if(sync == NULL) { FREEPKG(dummy); pm_errno = PM_ERR_MEMORY; goto error; } trans->packages = _alpm_list_add(trans->packages, sync); } else { /* spkg->name is already in the packages list -- just ignore it */ } } } return(0); error: return(-1); } int _alpm_sync_addtarget(pmtrans_t *trans, pmdb_t *db_local, PMList *dbs_sync, char *name) { char targline[PKG_FULLNAME_LEN]; char *targ; PMList *j; pmpkg_t *local; pmpkg_t *spkg = NULL; pmsyncpkg_t *sync; int cmp; ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); ASSERT(name != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); STRNCPY(targline, name, PKG_FULLNAME_LEN); targ = strchr(targline, '/'); if(targ) { *targ = '\0'; targ++; for(j = dbs_sync; j && !spkg; j = j->next) { pmdb_t *dbs = j->data; if(strcmp(dbs->treename, targline) == 0) { spkg = _alpm_db_get_pkgfromcache(dbs, targ); if(spkg == NULL) { /* Search provides */ PMList *p; _alpm_log(PM_LOG_FLOW2, "target '%s' not found -- looking for provisions", targ); p = _alpm_db_whatprovides(dbs, targ); if(p == NULL) { RET_ERR(PM_ERR_PKG_NOT_FOUND, -1); } _alpm_log(PM_LOG_DEBUG, "found '%s' as a provision for '%s'", p->data, targ); spkg = _alpm_db_get_pkgfromcache(dbs, p->data); FREELISTPTR(p); } } } } else { targ = targline; for(j = dbs_sync; j && !spkg; j = j->next) { pmdb_t *dbs = j->data; spkg = _alpm_db_get_pkgfromcache(dbs, targ); } if(spkg == NULL) { /* Search provides */ _alpm_log(PM_LOG_FLOW2, "target '%s' not found -- looking for provisions", targ); for(j = dbs_sync; j && !spkg; j = j->next) { pmdb_t *dbs = j->data; PMList *p = _alpm_db_whatprovides(dbs, targ); if(p) { _alpm_log(PM_LOG_DEBUG, "found '%s' as a provision for '%s'", p->data, targ); spkg = _alpm_db_get_pkgfromcache(dbs, p->data); FREELISTPTR(p); } } } } if(spkg == NULL) { RET_ERR(PM_ERR_PKG_NOT_FOUND, -1); } local = _alpm_db_get_pkgfromcache(db_local, spkg->name); if(local) { cmp = _alpm_versioncmp(local->version, spkg->version); if(cmp > 0) { /* local version is newer -- get confirmation before adding */ int resp = 0; QUESTION(trans, PM_TRANS_CONV_LOCAL_NEWER, local, NULL, NULL, &resp); if(!resp) { _alpm_log(PM_LOG_WARNING, "%s-%s: local version is newer -- skipping", local->name, local->version); return(0); } } else if(cmp == 0) { /* versions are identical -- get confirmation before adding */ int resp = 0; QUESTION(trans, PM_TRANS_CONV_LOCAL_UPTODATE, local, NULL, NULL, &resp); if(!resp) { _alpm_log(PM_LOG_WARNING, "%s-%s is up to date -- skipping", local->name, local->version); return(0); } } } /* add the package to the transaction */ if(!find_pkginsync(spkg->name, trans->packages)) { pmpkg_t *dummy = NULL; if(local) { dummy = _alpm_pkg_new(local->name, local->version); if(dummy == NULL) { RET_ERR(PM_ERR_MEMORY, -1); } } sync = _alpm_sync_new(PM_SYNC_TYPE_UPGRADE, spkg, dummy); if(sync == NULL) { FREEPKG(dummy); RET_ERR(PM_ERR_MEMORY, -1); } _alpm_log(PM_LOG_FLOW2, "adding target '%s' to the transaction set", spkg->name); trans->packages = _alpm_list_add(trans->packages, sync); } return(0); } /* Helper function for _alpm_list_remove */ static int ptr_cmp(const void *s1, const void *s2) { return(strcmp(((pmsyncpkg_t *)s1)->pkg->name, ((pmsyncpkg_t *)s2)->pkg->name)); } int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, PMList *dbs_sync, PMList **data) { PMList *deps = NULL; PMList *list = NULL; /* list allowing checkdeps usage with data from trans->packages */ PMList *trail = NULL; /* breadcrum list to avoid running into circles */ PMList *asked = NULL; PMList *i, *j; ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); if(data) { *data = NULL; } if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) { for(i = trans->packages; i; i = i->next) { pmsyncpkg_t *sync = i->data; list = _alpm_list_add(list, sync->pkg); } trail = _alpm_list_new(); /* Resolve targets dependencies */ EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_START, NULL, NULL); _alpm_log(PM_LOG_FLOW1, "resolving targets dependencies"); for(i = trans->packages; i; i = i->next) { pmpkg_t *spkg = ((pmsyncpkg_t *)i->data)->pkg; if(_alpm_resolvedeps(db_local, dbs_sync, spkg, list, trail, trans, data) == -1) { /* pm_errno is set by resolvedeps */ goto error; } } for(i = list; i; i = i->next) { /* add the dependencies found by resolvedeps to the transaction set */ pmpkg_t *spkg = i->data; if(!find_pkginsync(spkg->name, trans->packages)) { pmsyncpkg_t *sync = _alpm_sync_new(PM_SYNC_TYPE_DEPEND, spkg, NULL); if(sync == NULL) { goto error; } trans->packages = _alpm_list_add(trans->packages, sync); _alpm_log(PM_LOG_FLOW2, "adding package %s-%s to the transaction targets", spkg->name, spkg->version); } } EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_DONE, NULL, NULL); /* check for inter-conflicts and whatnot */ EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_START, NULL, NULL); _alpm_log(PM_LOG_FLOW1, "looking for unresolvable dependencies"); deps = _alpm_checkdeps(db_local, PM_TRANS_TYPE_UPGRADE, list); if(deps) { if(data) { *data = deps; deps = NULL; } pm_errno = PM_ERR_UNSATISFIED_DEPS; goto error; } /* no unresolvable deps, so look for conflicts */ _alpm_log(PM_LOG_FLOW1, "looking for conflicts"); deps = _alpm_checkconflicts(db_local, list); if(deps) { int errorout = 0; for(i = deps; i && !errorout; i = i->next) { pmdepmissing_t *miss = i->data; int found = 0; pmsyncpkg_t *sync; pmpkg_t *local; _alpm_log(PM_LOG_FLOW2, "package '%s' is conflicting with '%s'", miss->target, miss->depend.name); /* check if the conflicting package is one that's about to be removed/replaced. * if so, then just ignore it */ for(j = trans->packages; j && !found; j = j->next) { sync = j->data; if(sync->type == PM_SYNC_TYPE_REPLACE) { if(_alpm_pkg_isin(miss->depend.name, sync->data)) { found = 1; } } } if(found) { _alpm_log(PM_LOG_FLOW2, "'%s' is already elected for removal -- skipping", miss->depend.name); continue; } sync = find_pkginsync(miss->target, trans->packages); if(sync == NULL) { _alpm_log(PM_LOG_DEBUG, "'%s' not found in transaction set -- skipping", miss->target); continue; } local = _alpm_db_get_pkgfromcache(db_local, miss->depend.name); /* check if this package also "provides" the package it's conflicting with */ if(_alpm_list_is_strin(miss->depend.name, sync->pkg->provides)) { /* so just treat it like a "replaces" item so the REQUIREDBY * fields are inherited properly. */ _alpm_log(PM_LOG_DEBUG, "package '%s' provides its own conflict", miss->target); if(local) { /* nothing to do for now: it will be handled later * (not the same behavior as in pacman 2.x) */ } else { char *rmpkg = NULL; int target, depend; /* hmmm, depend.name isn't installed, so it must be conflicting * with another package in our final list. For example: * * pacman -S blackbox xfree86 * * If no x-servers are installed and blackbox pulls in xorg, then * xorg and xfree86 will conflict with each other. In this case, * we should follow the user's preference and rip xorg out of final, * opting for xfree86 instead. */ /* figure out which one was requested in targets. If they both were, * then it's still an unresolvable conflict. */ target = _alpm_list_is_strin(miss->target, trans->targets); depend = _alpm_list_is_strin(miss->depend.name, trans->targets); if(depend && !target) { _alpm_log(PM_LOG_DEBUG, "'%s' is in the target list -- keeping it", miss->depend.name); /* remove miss->target */ rmpkg = miss->target; } else if(target && !depend) { _alpm_log(PM_LOG_DEBUG, "'%s' is in the target list -- keeping it", miss->target); /* remove miss->depend.name */ rmpkg = miss->depend.name; } else { /* something's not right, bail out with a conflict error */ } if(rmpkg) { pmsyncpkg_t *rsync = find_pkginsync(rmpkg, trans->packages); pmsyncpkg_t *spkg = NULL; _alpm_log(PM_LOG_FLOW2, "removing '%s' from target list", rmpkg); trans->packages = _alpm_list_remove(trans->packages, rsync, ptr_cmp, (void **)&spkg); FREESYNC(spkg); continue; } } } /* It's a conflict -- see if they want to remove it */ _alpm_log(PM_LOG_DEBUG, "resolving package '%s' conflict", miss->target); if(local) { int doremove = 0; if(!_alpm_list_is_strin(miss->depend.name, asked)) { QUESTION(trans, PM_TRANS_CONV_CONFLICT_PKG, miss->target, miss->depend.name, NULL, &doremove); asked = _alpm_list_add(asked, strdup(miss->depend.name)); if(doremove) { pmsyncpkg_t *rsync = find_pkginsync(miss->depend.name, trans->packages); pmpkg_t *q = _alpm_pkg_new(miss->depend.name, NULL); if(q == NULL) { if(data) { FREELIST(*data); } goto error; } q->requiredby = _alpm_list_strdup(local->requiredby); if(sync->type != PM_SYNC_TYPE_REPLACE) { /* switch this sync type to REPLACE */ sync->type = PM_SYNC_TYPE_REPLACE; FREEPKG(sync->data); } /* append to the replaces list */ _alpm_log(PM_LOG_FLOW2, "electing '%s' for removal", miss->depend.name); sync->data = _alpm_list_add(sync->data, q); if(rsync) { /* remove it from the target list */ pmsyncpkg_t *spkg = NULL; _alpm_log(PM_LOG_FLOW2, "removing '%s' from target list", miss->depend.name); trans->packages = _alpm_list_remove(trans->packages, rsync, ptr_cmp, (void **)&spkg); FREESYNC(spkg); } } else { /* abort */ _alpm_log(PM_LOG_ERROR, "unresolvable package conflicts detected"); errorout = 1; if(data) { if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) { FREELIST(*data); pm_errno = PM_ERR_MEMORY; goto error; } *miss = *(pmdepmissing_t *)i->data; *data = _alpm_list_add(*data, miss); } } } } else { _alpm_log(PM_LOG_ERROR, "unresolvable package conflicts detected"); errorout = 1; if(data) { if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) { FREELIST(*data); pm_errno = PM_ERR_MEMORY; goto error; } *miss = *(pmdepmissing_t *)i->data; *data = _alpm_list_add(*data, miss); } } } if(errorout) { pm_errno = PM_ERR_CONFLICTING_DEPS; goto error; } FREELIST(deps); } EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_DONE, NULL, NULL); FREELISTPTR(list); FREELISTPTR(trail); FREELIST(asked); /* XXX: this fails for cases where a requested package wants * a dependency that conflicts with an older version of * the package. It will be removed from final, and the user * has to re-request it to get it installed properly. * * Not gonna happen very often, but should be dealt with... */ /* Check dependencies of packages in rmtargs and make sure * we won't be breaking anything by removing them. * If a broken dep is detected, make sure it's not from a * package that's in our final (upgrade) list. */ /*EVENT(trans, PM_TRANS_EVT_CHECKDEPS_DONE, NULL, NULL);*/ for(i = trans->packages; i; i = i->next) { pmsyncpkg_t *sync = i->data; if(sync->type == PM_SYNC_TYPE_REPLACE) { for(j = sync->data; j; j = j->next) { list = _alpm_list_add(list, j->data); } } } if(list) { _alpm_log(PM_LOG_FLOW1, "checking dependencies of packages designated for removal"); deps = _alpm_checkdeps(db_local, PM_TRANS_TYPE_REMOVE, list); if(deps) { int errorout = 0; for(i = deps; i; i = i->next) { pmdepmissing_t *miss = i->data; if(!find_pkginsync(miss->depend.name, trans->packages)) { int pfound = 0; PMList *k; /* If miss->depend.name depends on something that miss->target and a * package in final both provide, then it's okay... */ pmpkg_t *leavingp = _alpm_db_get_pkgfromcache(db_local, miss->target); pmpkg_t *conflictp = _alpm_db_get_pkgfromcache(db_local, miss->depend.name); if(!leavingp || !conflictp) { _alpm_log(PM_LOG_ERROR, "something has gone horribly wrong"); goto error; } /* Look through the upset package's dependencies and try to match one up * to a provisio from the package we want to remove */ for(k = conflictp->depends; k && !pfound; k = k->next) { PMList *m; for(m = leavingp->provides; m && !pfound; m = m->next) { if(!strcmp(k->data, m->data)) { /* Found a match -- now look through final for a package that * provides the same thing. If none are found, then it truly * is an unresolvable conflict. */ PMList *n, *o; for(n = trans->packages; n && !pfound; n = n->next) { pmsyncpkg_t *sp = n->data; for(o = sp->pkg->provides; o && !pfound; o = o->next) { if(!strcmp(m->data, o->data)) { /* found matching provisio -- we're good to go */ _alpm_log(PM_LOG_FLOW2, "found '%s' as a provision for '%s' -- conflict aborted", sp->pkg->name, (char *)o->data); pfound = 1; } } } } } } if(!pfound) { if(!errorout) { errorout = 1; } if(data) { if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) { FREELIST(*data); pm_errno = PM_ERR_MEMORY; goto error; } *miss = *(pmdepmissing_t *)i->data; *data = _alpm_list_add(*data, miss); } } } } if(errorout) { pm_errno = PM_ERR_UNSATISFIED_DEPS; goto error; } FREELIST(deps); } FREELISTPTR(list); } /*EVENT(trans, PM_TRANS_EVT_CHECKDEPS_DONE, NULL, NULL);*/ } return(0); error: FREELISTPTR(list); FREELISTPTR(trail); FREELIST(asked); FREELIST(deps); return(-1); } int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, PMList **data) { PMList *i; pmtrans_t *tr = NULL; int replaces = 0; ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); /* remove conflicting and to-be-replaced packages */ tr = _alpm_trans_new(); if(tr == NULL) { _alpm_log(PM_LOG_ERROR, "could not create removal transaction"); pm_errno = PM_ERR_MEMORY; goto error; } if(_alpm_trans_init(tr, PM_TRANS_TYPE_REMOVE, PM_TRANS_FLAG_NODEPS, trans->cb_event, trans->cb_conv) == -1) { _alpm_log(PM_LOG_ERROR, "could not initialize the removal transaction"); goto error; } for(i = trans->packages; i; i = i->next) { pmsyncpkg_t *sync = i->data; if(sync->type == PM_SYNC_TYPE_REPLACE) { PMList *j; for(j = sync->data; j; j = j->next) { pmpkg_t *pkg = j->data; if(!_alpm_pkg_isin(pkg->name, tr->packages)) { if(_alpm_trans_addtarget(tr, pkg->name) == -1) { goto error; } replaces++; } } } } if(replaces) { _alpm_log(PM_LOG_FLOW1, "removing conflicting and to-be-replaced packages"); if(_alpm_trans_prepare(tr, data) == -1) { _alpm_log(PM_LOG_ERROR, "could not prepare removal transaction"); goto error; } /* we want the frontend to be aware of commit details */ tr->cb_event = trans->cb_event; if(_alpm_trans_commit(tr, NULL) == -1) { _alpm_log(PM_LOG_ERROR, "could not commit removal transaction"); goto error; } } FREETRANS(tr); /* install targets */ _alpm_log(PM_LOG_FLOW1, "installing packages"); tr = _alpm_trans_new(); if(tr == NULL) { _alpm_log(PM_LOG_ERROR, "could not create transaction"); pm_errno = PM_ERR_MEMORY; goto error; } if(_alpm_trans_init(tr, PM_TRANS_TYPE_UPGRADE, trans->flags | PM_TRANS_FLAG_NODEPS, trans->cb_event, trans->cb_conv) == -1) { _alpm_log(PM_LOG_ERROR, "could not initialize transaction"); goto error; } for(i = trans->packages; i; i = i->next) { pmsyncpkg_t *sync = i->data; pmpkg_t *spkg = sync->pkg; char str[PATH_MAX]; snprintf(str, PATH_MAX, "%s%s/%s-%s" PM_EXT_PKG, handle->root, handle->cachedir, spkg->name, spkg->version); if(_alpm_trans_addtarget(tr, str) == -1) { goto error; } /* using _alpm_list_last() is ok because addtarget() adds the new target at the * end of the tr->packages list */ spkg = _alpm_list_last(tr->packages)->data; if(sync->type == PM_SYNC_TYPE_DEPEND) { spkg->reason = PM_PKG_REASON_DEPEND; } } if(_alpm_trans_prepare(tr, data) == -1) { _alpm_log(PM_LOG_ERROR, "could not prepare transaction"); goto error; } if(_alpm_trans_commit(tr, NULL) == -1) { _alpm_log(PM_LOG_ERROR, "could not commit transaction"); goto error; } FREETRANS(tr); /* propagate replaced packages' requiredby fields to their new owners */ if(replaces) { _alpm_log(PM_LOG_FLOW1, "updating database for replaced packages dependencies"); for(i = trans->packages; i; i = i->next) { pmsyncpkg_t *sync = i->data; if(sync->type == PM_SYNC_TYPE_REPLACE) { PMList *j; pmpkg_t *new = _alpm_db_get_pkgfromcache(db_local, sync->pkg->name); for(j = sync->data; j; j = j->next) { PMList *k; pmpkg_t *old = j->data; /* merge lists */ for(k = old->requiredby; k; k = k->next) { if(!_alpm_list_is_strin(k->data, new->requiredby)) { /* replace old's name with new's name in the requiredby's dependency list */ PMList *m; pmpkg_t *depender = _alpm_db_get_pkgfromcache(db_local, k->data); if(depender == NULL) { /* If the depending package no longer exists in the local db, * then it must have ALSO conflicted with sync->pkg. If * that's the case, then we don't have anything to propagate * here. */ continue; } for(m = depender->depends; m; m = m->next) { if(!strcmp(m->data, old->name)) { FREE(m->data); m->data = strdup(new->name); } } if(_alpm_db_write(db_local, depender, INFRQ_DEPENDS) == -1) { _alpm_log(PM_LOG_ERROR, "could not update requiredby for database entry %s-%s", new->name, new->version); } /* add the new requiredby */ new->requiredby = _alpm_list_add(new->requiredby, strdup(k->data)); } } } if(_alpm_db_write(db_local, new, INFRQ_DEPENDS) == -1) { _alpm_log(PM_LOG_ERROR, "could not update new database entry %s-%s", new->name, new->version); } } } } return(0); error: FREETRANS(tr); return(-1); } /* vim: set ts=2 sw=2 noet: */