4 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
5 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
6 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
7 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
8 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
34 #if defined(__APPLE__) || defined(__OpenBSD__)
35 #include <sys/syslimits.h>
37 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__sun__)
46 #include "alpm_list.h"
50 #include "versioncmp.h"
63 int _alpm_remove_loadtarget(pmtrans_t
*trans
, pmdb_t
*db
, char *name
)
69 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
70 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
71 ASSERT(name
!= NULL
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
73 if(_alpm_pkg_find(name
, trans
->packages
)) {
74 RET_ERR(PM_ERR_TRANS_DUP_TARGET
, -1);
77 if((info
= _alpm_db_scan(db
, name
)) == NULL
) {
78 /* Unimportant - just ignore it if we can't find it */
79 _alpm_log(PM_LOG_DEBUG
, _("could not find %s in database"), name
);
80 RET_ERR(PM_ERR_PKG_NOT_FOUND
, -1);
83 /* ignore holdpkgs on upgrade */
84 if((trans
== handle
->trans
)
85 && alpm_list_find_str(handle
->holdpkg
, info
->name
)) {
87 QUESTION(trans
, PM_TRANS_CONV_REMOVE_HOLDPKG
, info
, NULL
, NULL
, &resp
);
89 RET_ERR(PM_ERR_PKG_HOLD
, -1);
93 _alpm_log(PM_LOG_DEBUG
, _("adding %s in the targets list"), info
->name
);
94 trans
->packages
= alpm_list_add(trans
->packages
, info
);
99 int _alpm_remove_prepare(pmtrans_t
*trans
, pmdb_t
*db
, alpm_list_t
**data
)
105 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
106 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
108 if(!(trans
->flags
& (PM_TRANS_FLAG_NODEPS
)) && (trans
->type
!= PM_TRANS_TYPE_UPGRADE
)) {
109 EVENT(trans
, PM_TRANS_EVT_CHECKDEPS_START
, NULL
, NULL
);
111 _alpm_log(PM_LOG_DEBUG
, _("looking for unsatisfied dependencies"));
112 lp
= _alpm_checkdeps(trans
, db
, trans
->type
, trans
->packages
);
114 if(trans
->flags
& PM_TRANS_FLAG_CASCADE
) {
117 for(i
= lp
; i
; i
= i
->next
) {
118 pmdepmissing_t
*miss
= (pmdepmissing_t
*)i
->data
;
119 pmpkg_t
*info
= _alpm_db_scan(db
, miss
->depend
.name
);
121 _alpm_log(PM_LOG_DEBUG
, _("pulling %s in the targets list"), alpm_pkg_get_name(info
));
122 trans
->packages
= alpm_list_add(trans
->packages
, info
);
124 _alpm_log(PM_LOG_ERROR
, _("could not find %s in database -- skipping"),
129 lp
= _alpm_checkdeps(trans
, db
, trans
->type
, trans
->packages
);
137 RET_ERR(PM_ERR_UNSATISFIED_DEPS
, -1);
141 if(trans
->flags
& PM_TRANS_FLAG_RECURSE
) {
142 _alpm_log(PM_LOG_DEBUG
, _("finding removable dependencies"));
143 trans
->packages
= _alpm_removedeps(db
, trans
->packages
);
146 /* re-order w.r.t. dependencies */
147 _alpm_log(PM_LOG_DEBUG
, _("sorting by dependencies"));
148 lp
= _alpm_sortbydeps(trans
->packages
, PM_TRANS_TYPE_REMOVE
);
149 /* free the old alltargs */
150 FREELISTPTR(trans
->packages
);
151 trans
->packages
= lp
;
153 EVENT(trans
, PM_TRANS_EVT_CHECKDEPS_DONE
, NULL
, NULL
);
159 static int can_remove_file(pmtrans_t
*trans
, const char *path
)
161 char file
[PATH_MAX
+1];
163 snprintf(file
, PATH_MAX
, "%s%s", handle
->root
, path
);
165 if(alpm_list_find_str(trans
->skip_remove
, file
)) {
166 /* return success because we will never actually remove this file */
169 /* If we fail write permissions due to a read-only filesystem, abort.
170 * Assume all other possible failures are covered somewhere else */
171 if(access(file
, W_OK
) == -1) {
172 if(errno
!= EACCES
&& access(file
, F_OK
) == 0) {
173 /* only return failure if the file ACTUALLY exists and we can't write to
174 * it - ignore "chmod -w" simple permission failures */
175 _alpm_log(PM_LOG_ERROR
, _("cannot remove file '%s': %s"),
176 file
, strerror(errno
));
184 /* Helper function for iterating through a package's file and deleting them
185 * Used by _alpm_remove_commit
187 * TODO the parameters are a bit out of control here. This function doesn't
188 * need to report PROGRESS, do it in the parent function.
190 static void unlink_file(pmpkg_t
*info
, alpm_list_t
*lp
, alpm_list_t
*targ
,
191 pmtrans_t
*trans
, int filenum
, int *position
)
195 double percent
= 0.0;
196 char file
[PATH_MAX
+1];
201 percent
= (double)*position
/ filenum
;
204 char *hash
= _alpm_needbackup(lp
->data
, alpm_pkg_get_backup(info
));
210 if(trans
->type
== PM_TRANS_TYPE_UPGRADE
) {
211 /* check noupgrade */
212 if(alpm_list_find_str(handle
->noupgrade
, lp
->data
)) {
213 _alpm_log(PM_LOG_DEBUG
, _("Skipping removal of '%s' due to NoUpgrade"), file
);
218 snprintf(file
, PATH_MAX
, "%s%s", handle
->root
, (char *)lp
->data
);
219 if(lstat(file
, &buf
)) {
220 _alpm_log(PM_LOG_DEBUG
, _("file %s does not exist"), file
);
224 if(S_ISDIR(buf
.st_mode
)) {
226 /* this is okay, other pakcages are probably using it (like /usr) */
227 _alpm_log(PM_LOG_DEBUG
, _("keeping directory %s"), file
);
229 _alpm_log(PM_LOG_DEBUG
, _("removing directory %s"), file
);
232 /* check the remove skip list before removing the file.
233 * see the big comment block in db_find_conflicts() for an
235 if(alpm_list_find_str(trans
->skip_remove
, file
)) {
236 _alpm_log(PM_LOG_DEBUG
, _("%s is in trans->skip_remove, skipping removal"), file
);
238 } else if(needbackup
) {
239 /* if the file is flagged, back it up to .pacsave */
240 if(!(trans
->type
== PM_TRANS_TYPE_UPGRADE
)) {
241 /* if it was an upgrade, the file would be left alone because
242 * pacman_add() would handle it */
243 if(!(trans
->flags
& PM_TRANS_FLAG_NOSAVE
)) {
244 char newpath
[PATH_MAX
];
245 snprintf(newpath
, PATH_MAX
, "%s.pacsave", file
);
246 rename(file
, newpath
);
247 _alpm_log(PM_LOG_WARNING
, _("%s saved as %s"), file
, newpath
);
250 _alpm_log(PM_LOG_DEBUG
, _("transaction is set to NOSAVE, not backing up '%s'"), file
);
254 _alpm_log(PM_LOG_DEBUG
, _("unlinking %s"), file
);
255 int list_count
= alpm_list_count(trans
->packages
); /* this way we don't have to call alpm_list_count twice during PROGRESS */
257 PROGRESS(trans
, PM_TRANS_PROGRESS_REMOVE_START
, info
->name
, (double)(percent
* 100), list_count
, (list_count
- alpm_list_count(targ
) + 1));
260 if(unlink(file
) == -1) {
261 _alpm_log(PM_LOG_ERROR
, _("cannot remove file %s: %s"), lp
->data
, strerror(errno
));
266 int _alpm_remove_commit(pmtrans_t
*trans
, pmdb_t
*db
)
268 pmpkg_t
*info
, *infodup
;
269 alpm_list_t
*targ
, *lp
;
273 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
274 ASSERT(trans
!= NULL
, RET_ERR(PM_ERR_TRANS_NULL
, -1));
276 for(targ
= trans
->packages
; targ
; targ
= targ
->next
) {
278 char scriptlet
[PATH_MAX
];
280 info
= (pmpkg_t
*)targ
->data
;
281 const char *pkgname
= NULL
;
283 if(handle
->trans
->state
== STATE_INTERRUPTED
) {
287 /* get the name now so we can use it after package is removed */
288 pkgname
= alpm_pkg_get_name(info
);
289 snprintf(scriptlet
, PATH_MAX
, "%s%s-%s/install", db
->path
,
290 pkgname
, alpm_pkg_get_version(info
));
292 if(trans
->type
!= PM_TRANS_TYPE_UPGRADE
) {
293 EVENT(trans
, PM_TRANS_EVT_REMOVE_START
, info
, NULL
);
294 _alpm_log(PM_LOG_DEBUG
, _("removing package %s-%s"),
295 pkgname
, alpm_pkg_get_version(info
));
297 /* run the pre-remove scriptlet if it exists */
298 if(alpm_pkg_has_scriptlet(info
) && !(trans
->flags
& PM_TRANS_FLAG_NOSCRIPTLET
)) {
299 _alpm_runscriptlet(handle
->root
, scriptlet
, "pre_remove",
300 alpm_pkg_get_version(info
), NULL
, trans
);
304 files
= alpm_pkg_get_files(info
);
306 if(!(trans
->flags
& PM_TRANS_FLAG_DBONLY
)) {
307 for(lp
= files
; lp
; lp
= lp
->next
) {
308 if(!can_remove_file(trans
, lp
->data
)) {
309 _alpm_log(PM_LOG_DEBUG
, _("not removing package '%s', can't remove all files"),
311 RET_ERR(PM_ERR_PKG_CANT_REMOVE
, -1);
315 int filenum
= alpm_list_count(files
);
316 _alpm_log(PM_LOG_DEBUG
, _("removing %d files"), filenum
);
318 /* iterate through the list backwards, unlinking files */
319 for(lp
= alpm_list_last(files
); lp
; lp
= lp
->prev
) {
320 unlink_file(info
, lp
, targ
, trans
, filenum
, &position
);
324 if(trans
->type
!= PM_TRANS_TYPE_UPGRADE
) {
325 /* run the post-remove script if it exists */
326 if(alpm_pkg_has_scriptlet(info
) && !(trans
->flags
& PM_TRANS_FLAG_NOSCRIPTLET
)) {
327 _alpm_runscriptlet(handle
->root
, scriptlet
, "post_remove",
328 alpm_pkg_get_version(info
), NULL
, trans
);
332 /* duplicate the package so we can remove the requiredby fields later */
333 infodup
= _alpm_pkg_dup(info
);
335 /* remove the package from the database */
336 _alpm_log(PM_LOG_DEBUG
, _("updating database"));
337 _alpm_log(PM_LOG_DEBUG
, _("removing database entry '%s'"), pkgname
);
338 if(_alpm_db_remove(db
, info
) == -1) {
339 _alpm_log(PM_LOG_ERROR
, _("could not remove database entry %s-%s"),
340 pkgname
, alpm_pkg_get_version(info
));
342 /* remove the package from the cache */
343 if(_alpm_db_remove_pkgfromcache(db
, info
) == -1) {
344 _alpm_log(PM_LOG_ERROR
, _("could not remove entry '%s' from cache"),
348 /* update dependency packages' REQUIREDBY fields */
349 _alpm_trans_update_depends(trans
, infodup
);
350 _alpm_pkg_free(infodup
);
353 PROGRESS(trans
, PM_TRANS_PROGRESS_REMOVE_START
, pkgname
, 100,
354 alpm_list_count(trans
->packages
),
355 (alpm_list_count(trans
->packages
) - alpm_list_count(targ
) +1));
356 if(trans
->type
!= PM_TRANS_TYPE_UPGRADE
) {
357 EVENT(trans
, PM_TRANS_EVT_REMOVE_DONE
, info
, NULL
);
361 /* run ldconfig if it exists */
362 if((trans
->type
!= PM_TRANS_TYPE_UPGRADE
) && (handle
->trans
->state
!= STATE_INTERRUPTED
)) {
363 _alpm_log(PM_LOG_DEBUG
, _("running \"ldconfig -r %s\""), handle
->root
);
364 _alpm_ldconfig(handle
->root
);
370 /* vim: set ts=2 sw=2 noet: */