remove.c

Go to the documentation of this file.
00001 /*
00002  *  remove.c
00003  *
00004  *  Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org>
00005  *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
00006  *  Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
00007  *  Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
00008  *  Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022  */
00023 
00024 #include "config.h"
00025 
00026 #include <stdlib.h>
00027 #include <errno.h>
00028 #include <time.h>
00029 #include <fcntl.h>
00030 #include <string.h>
00031 #include <limits.h>
00032 #include <unistd.h>
00033 #include <sys/stat.h>
00034 
00035 /* libalpm */
00036 #include "remove.h"
00037 #include "alpm_list.h"
00038 #include "trans.h"
00039 #include "util.h"
00040 #include "error.h"
00041 #include "log.h"
00042 #include "backup.h"
00043 #include "package.h"
00044 #include "db.h"
00045 #include "cache.h"
00046 #include "deps.h"
00047 #include "handle.h"
00048 #include "alpm.h"
00049 
00050 int _alpm_remove_loadtarget(pmtrans_t *trans, pmdb_t *db, char *name)
00051 {
00052     pmpkg_t *info;
00053 
00054     ALPM_LOG_FUNC;
00055 
00056     ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00057     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00058     ASSERT(name != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
00059 
00060     if(_alpm_pkg_find(name, trans->packages)) {
00061         RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1);
00062     }
00063 
00064     if((info = _alpm_db_scan(db, name)) == NULL) {
00065         /* Unimportant - just ignore it if we can't find it */
00066         _alpm_log(PM_LOG_DEBUG, "could not find %s in database\n", name);
00067         RET_ERR(PM_ERR_PKG_NOT_FOUND, -1);
00068     }
00069 
00070     /* ignore holdpkgs on upgrade */
00071     if((trans == handle->trans)
00072         && alpm_list_find_str(handle->holdpkg, info->name)) {
00073         int resp = 0;
00074         QUESTION(trans, PM_TRANS_CONV_REMOVE_HOLDPKG, info, NULL, NULL, &resp);
00075         if(!resp) {
00076             RET_ERR(PM_ERR_PKG_HOLD, -1);
00077         }
00078     }
00079 
00080     _alpm_log(PM_LOG_DEBUG, "adding %s in the targets list\n", info->name);
00081     trans->packages = alpm_list_add(trans->packages, info);
00082 
00083     return(0);
00084 }
00085 
00086 int _alpm_remove_prepare(pmtrans_t *trans, pmdb_t *db, alpm_list_t **data)
00087 {
00088     alpm_list_t *lp;
00089 
00090     ALPM_LOG_FUNC;
00091 
00092     ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00093     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00094 
00095     /* skip all checks if we are doing this removal as part of an upgrade */
00096     if(trans->type == PM_TRANS_TYPE_REMOVEUPGRADE) {
00097         return(0);
00098     }
00099 
00100     if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
00101         EVENT(trans, PM_TRANS_EVT_CHECKDEPS_START, NULL, NULL);
00102 
00103         _alpm_log(PM_LOG_DEBUG, "looking for unsatisfied dependencies\n");
00104         lp = alpm_checkdeps(db, 1, trans->packages, NULL);
00105         if(lp != NULL) {
00106             if(trans->flags & PM_TRANS_FLAG_CASCADE) {
00107                 while(lp) {
00108                     alpm_list_t *i;
00109                     for(i = lp; i; i = i->next) {
00110                         pmdepmissing_t *miss = (pmdepmissing_t *)i->data;
00111                         pmpkg_t *info = _alpm_db_scan(db, miss->target);
00112                         if(info) {
00113                             if(!_alpm_pkg_find(alpm_pkg_get_name(info), trans->packages)) {
00114                                 _alpm_log(PM_LOG_DEBUG, "pulling %s in the targets list\n",
00115                                         alpm_pkg_get_name(info));
00116                                 trans->packages = alpm_list_add(trans->packages, info);
00117                             }
00118                         } else {
00119                             _alpm_log(PM_LOG_ERROR, _("could not find %s in database -- skipping\n"),
00120                                       miss->target);
00121                         }
00122                     }
00123                     alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free);
00124                     alpm_list_free(lp);
00125                     lp = alpm_checkdeps(db, 1, trans->packages, NULL);
00126                 }
00127             } else {
00128                 if(data) {
00129                     *data = lp;
00130                 } else {
00131                     FREELIST(lp);
00132                 }
00133                 RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1);
00134             }
00135         }
00136     }
00137 
00138     /* re-order w.r.t. dependencies */
00139     _alpm_log(PM_LOG_DEBUG, "sorting by dependencies\n");
00140     lp = _alpm_sortbydeps(trans->packages, PM_TRANS_TYPE_REMOVE);
00141     /* free the old alltargs */
00142     alpm_list_free(trans->packages);
00143     trans->packages = lp;
00144 
00145     if(trans->flags & PM_TRANS_FLAG_RECURSE) {
00146         _alpm_log(PM_LOG_DEBUG, "finding removable dependencies\n");
00147         _alpm_recursedeps(db, trans->packages, 0);
00148     }
00149 
00150     if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
00151         EVENT(trans, PM_TRANS_EVT_CHECKDEPS_DONE, NULL, NULL);
00152     }
00153 
00154     return(0);
00155 }
00156 
00157 static int can_remove_file(pmtrans_t *trans, const char *path)
00158 {
00159     char file[PATH_MAX+1];
00160 
00161     snprintf(file, PATH_MAX, "%s%s", handle->root, path);
00162 
00163     if(alpm_list_find_str(trans->skip_remove, file)) {
00164         /* return success because we will never actually remove this file */
00165         return(1);
00166     }
00167     /* If we fail write permissions due to a read-only filesystem, abort.
00168      * Assume all other possible failures are covered somewhere else */
00169     if(access(file, W_OK) == -1) {
00170         if(errno != EACCES && errno != ETXTBSY && access(file, F_OK) == 0) {
00171             /* only return failure if the file ACTUALLY exists and we can't write to
00172              * it - ignore "chmod -w" simple permission failures */
00173             _alpm_log(PM_LOG_ERROR, _("cannot remove file '%s': %s\n"),
00174                       file, strerror(errno));
00175             return(0);
00176         }
00177     }
00178 
00179     return(1);
00180 }
00181 
00182 /* Helper function for iterating through a package's file and deleting them
00183  * Used by _alpm_remove_commit. */
00184 static void unlink_file(pmpkg_t *info, alpm_list_t *lp, pmtrans_t *trans)
00185 {
00186     struct stat buf;
00187     int needbackup = 0;
00188     char file[PATH_MAX+1];
00189 
00190     ALPM_LOG_FUNC;
00191 
00192     char *hash = _alpm_needbackup(lp->data, alpm_pkg_get_backup(info));
00193     if(hash) {
00194         needbackup = 1;
00195         FREE(hash);
00196     }
00197 
00198     snprintf(file, PATH_MAX, "%s%s", handle->root, (char *)lp->data);
00199 
00200     if(trans->type == PM_TRANS_TYPE_REMOVEUPGRADE) {
00201         /* check noupgrade */
00202         if(alpm_list_find_str(handle->noupgrade, lp->data)) {
00203             _alpm_log(PM_LOG_DEBUG, "Skipping removal of '%s' due to NoUpgrade\n",
00204                     file);
00205             return;
00206         }
00207     }
00208 
00209     /* we want to do a lstat here, and not a _alpm_lstat.
00210      * if a directory in the package is actually a directory symlink on the
00211      * filesystem, we want to work with the linked directory instead of the
00212      * actual symlink */
00213     if(lstat(file, &buf)) {
00214         _alpm_log(PM_LOG_DEBUG, "file %s does not exist\n", file);
00215         return;
00216     }
00217 
00218     if(S_ISDIR(buf.st_mode)) {
00219         if(rmdir(file)) {
00220             /* this is okay, other packages are probably using it (like /usr) */
00221             _alpm_log(PM_LOG_DEBUG, "keeping directory %s\n", file);
00222         } else {
00223             _alpm_log(PM_LOG_DEBUG, "removing directory %s\n", file);
00224         }
00225     } else {
00226         /* check the remove skip list before removing the file.
00227          * see the big comment block in db_find_fileconflicts() for an
00228          * explanation. */
00229         if(alpm_list_find_str(trans->skip_remove, file)) {
00230             _alpm_log(PM_LOG_DEBUG, "%s is in trans->skip_remove, skipping removal\n",
00231                     file);
00232             return;
00233         } else if(needbackup) {
00234             /* if the file is flagged, back it up to .pacsave */
00235             if(!(trans->flags & PM_TRANS_FLAG_NOSAVE)) {
00236                 char newpath[PATH_MAX];
00237                 snprintf(newpath, PATH_MAX, "%s.pacsave", file);
00238                 rename(file, newpath);
00239                 _alpm_log(PM_LOG_WARNING, _("%s saved as %s\n"), file, newpath);
00240                 return;
00241             } else {
00242                 _alpm_log(PM_LOG_DEBUG, "transaction is set to NOSAVE, not backing up '%s'\n", file);
00243             }
00244         }
00245         _alpm_log(PM_LOG_DEBUG, "unlinking %s\n", file);
00246 
00247         if(unlink(file) == -1) {
00248             _alpm_log(PM_LOG_ERROR, _("cannot remove file '%s': %s\n"),
00249                                 (char *)lp->data, strerror(errno));
00250         }
00251     }
00252 }
00253 
00254 int _alpm_remove_commit(pmtrans_t *trans, pmdb_t *db)
00255 {
00256     pmpkg_t *info;
00257     alpm_list_t *targ, *lp;
00258     int pkg_count;
00259 
00260     ALPM_LOG_FUNC;
00261 
00262     ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00263     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00264 
00265     pkg_count = alpm_list_count(trans->packages);
00266 
00267     for(targ = trans->packages; targ; targ = targ->next) {
00268         int position = 0;
00269         char scriptlet[PATH_MAX];
00270         alpm_list_t *files;
00271         info = (pmpkg_t*)targ->data;
00272         const char *pkgname = NULL;
00273 
00274         if(handle->trans->state == STATE_INTERRUPTED) {
00275             return(0);
00276         }
00277 
00278         /* get the name now so we can use it after package is removed */
00279         pkgname = alpm_pkg_get_name(info);
00280         snprintf(scriptlet, PATH_MAX, "%s%s-%s/install", db->path,
00281                          pkgname, alpm_pkg_get_version(info));
00282 
00283         if(trans->type != PM_TRANS_TYPE_REMOVEUPGRADE) {
00284             EVENT(trans, PM_TRANS_EVT_REMOVE_START, info, NULL);
00285             _alpm_log(PM_LOG_DEBUG, "removing package %s-%s\n",
00286                                 pkgname, alpm_pkg_get_version(info));
00287 
00288             /* run the pre-remove scriptlet if it exists  */
00289             if(alpm_pkg_has_scriptlet(info) && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
00290                 _alpm_runscriptlet(handle->root, scriptlet, "pre_remove",
00291                                    alpm_pkg_get_version(info), NULL, trans);
00292             }
00293         }
00294 
00295         files = alpm_pkg_get_files(info);
00296 
00297         if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) {
00298             for(lp = files; lp; lp = lp->next) {
00299                 if(!can_remove_file(trans, lp->data)) {
00300                     _alpm_log(PM_LOG_DEBUG, "not removing package '%s', can't remove all files\n",
00301                               pkgname);
00302                     RET_ERR(PM_ERR_PKG_CANT_REMOVE, -1);
00303                 }
00304             }
00305 
00306             int filenum = alpm_list_count(files);
00307             double percent = 0.0;
00308             alpm_list_t *newfiles;
00309             _alpm_log(PM_LOG_DEBUG, "removing %d files\n", filenum);
00310 
00311             /* iterate through the list backwards, unlinking files */
00312             newfiles = alpm_list_reverse(files);
00313             for(lp = newfiles; lp; lp = alpm_list_next(lp)) {
00314                 unlink_file(info, lp, trans);
00315 
00316                 /* update progress bar after each file */
00317                 percent = (double)position / (double)filenum;
00318                 PROGRESS(trans, PM_TRANS_PROGRESS_REMOVE_START, info->name,
00319                         (double)(percent * 100), pkg_count,
00320                         (pkg_count - alpm_list_count(targ) + 1));
00321                 position++;
00322             }
00323             alpm_list_free(newfiles);
00324         }
00325 
00326         /* set progress to 100% after we finish unlinking files */
00327         PROGRESS(trans, PM_TRANS_PROGRESS_REMOVE_START, pkgname, 100,
00328                  pkg_count, (pkg_count - alpm_list_count(targ) + 1));
00329 
00330         if(trans->type != PM_TRANS_TYPE_REMOVEUPGRADE) {
00331             /* run the post-remove script if it exists  */
00332             if(alpm_pkg_has_scriptlet(info) && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
00333                 _alpm_runscriptlet(handle->root, scriptlet, "post_remove",
00334                                                      alpm_pkg_get_version(info), NULL, trans);
00335             }
00336         }
00337 
00338         /* remove the package from the database */
00339         _alpm_log(PM_LOG_DEBUG, "updating database\n");
00340         _alpm_log(PM_LOG_DEBUG, "removing database entry '%s'\n", pkgname);
00341         if(_alpm_db_remove(db, info) == -1) {
00342             _alpm_log(PM_LOG_ERROR, _("could not remove database entry %s-%s\n"),
00343                       pkgname, alpm_pkg_get_version(info));
00344         }
00345         /* remove the package from the cache */
00346         if(_alpm_db_remove_pkgfromcache(db, info) == -1) {
00347             _alpm_log(PM_LOG_ERROR, _("could not remove entry '%s' from cache\n"),
00348                       pkgname);
00349         }
00350 
00351         /* call a done event if this isn't an upgrade */
00352         if(trans->type != PM_TRANS_TYPE_REMOVEUPGRADE) {
00353             EVENT(trans, PM_TRANS_EVT_REMOVE_DONE, info, NULL);
00354         }
00355     }
00356 
00357     /* run ldconfig if it exists */
00358     if(trans->type != PM_TRANS_TYPE_REMOVEUPGRADE) {
00359         _alpm_log(PM_LOG_DEBUG, "running \"ldconfig -r %s\"\n", handle->root);
00360         _alpm_ldconfig(handle->root);
00361     }
00362 
00363     return(0);
00364 }
00365 
00366 /* vim: set ts=2 sw=2 noet: */

Generated on Mon Jan 14 23:53:40 2008 for libalpm by  doxygen 1.5.4