1 /* opkg_remove.c - the opkg package management system
5 Copyright (C) 2001 University of Southern California
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2, or (at
10 your option) any later version.
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
22 #include "opkg_message.h"
23 #include "opkg_remove.h"
25 #include "file_util.h"
26 #include "sprintf_alloc.h"
27 #include "libbb/libbb.h"
30 * Returns number of the number of packages depending on the packages provided by this package.
31 * Every package implicitly provides itself.
33 int pkg_has_installed_dependents(pkg_t
* pkg
, abstract_pkg_t
*** pdependents
)
35 abstract_pkg_t
**provider
, **provides
= pkg_get_ptr(pkg
, PKG_PROVIDES
);
36 unsigned int i
, n_installed_dependents
= 0;
40 while (provider
&& *provider
) {
41 abstract_pkg_t
*providee
= *provider
++;
42 abstract_pkg_t
**dependers
= providee
->depended_upon_by
;
43 abstract_pkg_t
*dep_ab_pkg
;
44 if (dependers
== NULL
)
46 while ((dep_ab_pkg
= *dependers
++) != NULL
) {
47 if (dep_ab_pkg
->state_status
== SS_INSTALLED
) {
48 n_installed_dependents
++;
53 /* if caller requested the set of installed dependents */
56 abstract_pkg_t
**dependents
=
57 xcalloc((n_installed_dependents
+ 1),
58 sizeof(abstract_pkg_t
*));
61 *pdependents
= dependents
;
63 while (provider
&& *provider
) {
64 abstract_pkg_t
*providee
= *provider
++;
65 abstract_pkg_t
**dependers
= providee
->depended_upon_by
;
66 abstract_pkg_t
*dep_ab_pkg
;
67 if (dependers
== NULL
)
69 while ((dep_ab_pkg
= *dependers
++) != NULL
) {
70 if (dep_ab_pkg
->state_status
== SS_INSTALLED
71 && !(dep_ab_pkg
->state_flag
& SF_MARKED
)) {
72 dependents
[p
++] = dep_ab_pkg
;
73 dep_ab_pkg
->state_flag
|= SF_MARKED
;
78 /* now clear the marks */
79 for (i
= 0; i
< p
; i
++) {
80 abstract_pkg_t
*dep_ab_pkg
= dependents
[i
];
81 dep_ab_pkg
->state_flag
&= ~SF_MARKED
;
84 return n_installed_dependents
;
87 static int opkg_remove_dependent_pkgs(pkg_t
* pkg
, abstract_pkg_t
** dependents
)
92 pkg_vec_t
*dependent_pkgs
;
93 abstract_pkg_t
*ab_pkg
;
95 if ((ab_pkg
= pkg
->parent
) == NULL
) {
96 opkg_msg(ERROR
, "Internal error: pkg %s isn't in hash table\n",
101 if (dependents
== NULL
)
104 // here i am using the dependencies_checked
105 if (ab_pkg
->dependencies_checked
== 2) // variable to make out whether this package
106 return 0; // has already been encountered in the process
107 // of marking packages for removal - Karthik
108 ab_pkg
->dependencies_checked
= 2;
112 dependent_pkgs
= pkg_vec_alloc();
114 while (dependents
[i
] != NULL
) {
115 abstract_pkg_t
*dep_ab_pkg
= dependents
[i
];
117 if (dep_ab_pkg
->dependencies_checked
== 2) {
121 if (dep_ab_pkg
->state_status
== SS_INSTALLED
) {
122 for (a
= 0; a
< dep_ab_pkg
->pkgs
->len
; a
++) {
123 pkg_t
*dep_pkg
= dep_ab_pkg
->pkgs
->pkgs
[a
];
124 if (dep_pkg
->state_status
== SS_INSTALLED
) {
125 pkg_vec_insert(dependent_pkgs
, dep_pkg
);
131 /* 1 - to keep track of visited ab_pkgs when checking for possiblility of a broken removal of pkgs.
132 * 2 - to keep track of pkgs whose deps have been checked alrdy - Karthik */
136 pkg_vec_free(dependent_pkgs
);
141 for (i
= 0; i
< dependent_pkgs
->len
; i
++) {
142 err
= opkg_remove_pkg(dependent_pkgs
->pkgs
[i
], 0);
147 pkg_vec_free(dependent_pkgs
);
151 static void print_dependents_warning(pkg_t
* pkg
, abstract_pkg_t
** dependents
)
153 abstract_pkg_t
*dep_ab_pkg
;
154 opkg_msg(ERROR
, "Package %s is depended upon by packages:\n",
156 while ((dep_ab_pkg
= *dependents
++) != NULL
) {
157 if (dep_ab_pkg
->state_status
== SS_INSTALLED
)
158 opkg_msg(ERROR
, "\t%s\n", dep_ab_pkg
->name
);
161 "These might cease to work if package %s is removed.\n\n",
164 "Force removal of this package with --force-depends.\n");
165 opkg_msg(ERROR
, "Force removal of this package and its dependents\n");
166 opkg_msg(ERROR
, "with --force-removal-of-dependent-packages.\n");
170 * Find and remove packages that were autoinstalled and are orphaned
171 * by the removal of pkg.
173 static int remove_autoinstalled(pkg_t
* pkg
)
179 struct compound_depend
*cdep
;
180 abstract_pkg_t
**dependents
;
182 for (cdep
= pkg_get_ptr(pkg
, PKG_DEPENDS
); cdep
&& cdep
->type
; cdep
++) {
183 if (cdep
->type
!= PREDEPEND
184 && cdep
->type
!= DEPEND
&& cdep
->type
!= RECOMMEND
)
186 for (j
= 0; j
< cdep
->possibility_count
; j
++) {
187 p
= pkg_hash_fetch_installed_by_name(cdep
->
191 /* If the package is not installed, this could have
192 * been a circular dependency and the package has
193 * already been removed.
198 if (!p
->auto_installed
)
201 n_deps
= pkg_has_installed_dependents(p
, &dependents
);
203 opkg_msg(NOTICE
, "%s was autoinstalled and is "
204 "now orphaned, removing.\n", p
->name
);
205 if (opkg_remove_pkg(p
, 0) != 0) {
209 opkg_msg(INFO
, "%s was autoinstalled and is "
210 "still required by %d "
211 "installed packages.\n",
222 int opkg_remove_pkg(pkg_t
* pkg
, int from_upgrade
)
225 abstract_pkg_t
*parent_pkg
= NULL
;
228 * If called from an upgrade and not from a normal remove,
229 * ignore the essential flag.
231 if (pkg
->essential
&& !from_upgrade
) {
232 if (conf
->force_removal_of_essential_packages
) {
234 "Removing essential package %s under your coercion.\n"
235 "\tIf your system breaks, you get to keep both pieces\n",
239 "Refusing to remove essential package %s.\n"
240 "\tRemoving an essential package may lead to an unusable system, but if\n"
241 "\tyou enjoy that kind of pain, you can force opkg to proceed against\n"
242 "\tits will with the option: --force-removal-of-essential-packages\n",
248 if ((parent_pkg
= pkg
->parent
) == NULL
)
251 /* only attempt to remove dependent installed packages if
252 * force_depends is not specified or the package is being
255 if (!conf
->force_depends
&& !(pkg
->state_flag
& SF_REPLACE
)) {
256 abstract_pkg_t
**dependents
;
257 int has_installed_dependents
=
258 pkg_has_installed_dependents(pkg
, &dependents
);
260 if (has_installed_dependents
) {
262 * if this package is depended upon by others, then either we should
263 * not remove it or we should remove it and all of its dependents
266 if (!conf
->force_removal_of_dependent_packages
) {
267 print_dependents_warning(pkg
, dependents
);
272 /* remove packages depending on this package - Karthik */
273 err
= opkg_remove_dependent_pkgs(pkg
, dependents
);
283 if (from_upgrade
== 0) {
284 opkg_msg(NOTICE
, "Removing package %s from %s...\n",
285 pkg
->name
, pkg
->dest
->name
);
287 pkg
->state_flag
|= SF_FILELIST_CHANGED
;
289 pkg
->state_want
= SW_DEINSTALL
;
290 opkg_state_changed
++;
292 if (pkg_run_script(pkg
, "prerm", "remove") != 0) {
293 if (!conf
->force_remove
) {
294 opkg_msg(ERROR
, "not removing package \"%s\", "
295 "prerm script failed\n", pkg
->name
);
297 "You can force removal of packages with failed "
298 "prerm scripts with the option: \n"
299 "\t--force-remove\n");
304 /* DPKG_INCOMPATIBILITY: dpkg is slightly different here. It
305 maintains an empty filelist rather than deleting it. That seems
306 like a big pain, and I don't see that that should make a big
307 difference, but for anyone who wants tighter compatibility,
308 feel free to fix this. */
309 remove_data_files_and_list(pkg
);
311 err
= pkg_run_script(pkg
, "postrm", "remove");
313 remove_maintainer_scripts(pkg
);
314 pkg
->state_status
= SS_NOT_INSTALLED
;
317 parent_pkg
->state_status
= SS_NOT_INSTALLED
;
319 /* remove autoinstalled packages that are orphaned by the removal of this one */
320 if (conf
->autoremove
) {
321 if (remove_autoinstalled(pkg
) != 0) {
328 void remove_data_files_and_list(pkg_t
* pkg
)
330 str_list_t installed_dirs
;
331 str_list_t
*installed_files
;
332 str_list_elt_t
*iter
;
334 conffile_t
*conffile
;
339 installed_files
= pkg_get_installed_files(pkg
);
340 if (installed_files
== NULL
) {
341 opkg_msg(ERROR
, "Failed to determine installed "
342 "files for %s. None removed.\n", pkg
->name
);
346 str_list_init(&installed_dirs
);
348 /* don't include trailing slash */
349 if (conf
->offline_root
)
350 rootdirlen
= strlen(conf
->offline_root
);
352 for (iter
= str_list_first(installed_files
); iter
;
353 iter
= str_list_next(installed_files
, iter
)) {
354 file_name
= (char *)iter
->data
;
356 owner
= file_hash_get_file_owner(file_name
);
358 /* File may have been claimed by another package. */
361 if (file_is_dir(file_name
)) {
362 str_list_append(&installed_dirs
, file_name
);
366 conffile
= pkg_get_conffile(pkg
, file_name
+ rootdirlen
);
368 if (conffile_has_been_modified(conffile
)) {
370 "Not deleting modified conffile %s.\n",
376 if (!conf
->noaction
) {
377 opkg_msg(INFO
, "Deleting %s.\n", file_name
);
380 opkg_msg(INFO
, "Not deleting %s. (noaction)\n",
383 file_hash_remove(file_name
);
386 /* Remove empty directories */
387 if (!conf
->noaction
) {
390 for (iter
= str_list_first(&installed_dirs
); iter
;
391 iter
= str_list_next(&installed_dirs
, iter
)) {
392 file_name
= (char *)iter
->data
;
394 if (rmdir(file_name
) == 0) {
395 opkg_msg(INFO
, "Deleting %s.\n",
398 str_list_remove(&installed_dirs
, &iter
);
401 } while (removed_a_dir
);
404 pkg_free_installed_files(pkg
);
405 pkg_remove_installed_files_list(pkg
);
407 /* Don't print warning for dirs that are provided by other packages */
408 for (iter
= str_list_first(&installed_dirs
); iter
;
409 iter
= str_list_next(&installed_dirs
, iter
)) {
410 file_name
= (char *)iter
->data
;
412 owner
= file_hash_get_file_owner(file_name
);
416 str_list_remove(&installed_dirs
, &iter
);
421 while (!void_list_empty(&installed_dirs
)) {
422 iter
= str_list_pop(&installed_dirs
);
426 str_list_deinit(&installed_dirs
);
429 void remove_maintainer_scripts(pkg_t
* pkg
)
438 sprintf_alloc(&globpattern
, "%s/%s.*", pkg
->dest
->info_dir
, pkg
->name
);
440 err
= glob(globpattern
, 0, NULL
, &globbuf
);
445 for (i
= 0; i
< globbuf
.gl_pathc
; i
++) {
446 opkg_msg(INFO
, "Deleting %s.\n", globbuf
.gl_pathv
[i
]);
447 unlink(globbuf
.gl_pathv
[i
]);