1 /* opkg_remove.c - the opkg package management system
2 
3    Carl D. Worth
4 
5    Copyright (C) 2001 University of Southern California
6 
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.
11 
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.
16 */
17 
18 #include <stdio.h>
19 #include <glob.h>
20 #include <unistd.h>
21 
22 #include "opkg_message.h"
23 #include "opkg_remove.h"
24 #include "opkg_cmd.h"
25 #include "pkg_alternatives.h"
26 #include "file_util.h"
27 #include "sprintf_alloc.h"
28 #include "libbb/libbb.h"
29 
30 /*
31  * Returns number of the number of packages depending on the packages provided by this package.
32  * Every package implicitly provides itself.
33  */
pkg_has_installed_dependents(pkg_t * pkg,abstract_pkg_t *** pdependents)34 int pkg_has_installed_dependents(pkg_t * pkg, abstract_pkg_t *** pdependents)
35 {
36 	abstract_pkg_t **provider, **provides = pkg_get_ptr(pkg, PKG_PROVIDES);
37 	unsigned int i, n_installed_dependents = 0;
38 
39 	provider = provides;
40 
41 	while (provider && *provider) {
42 		abstract_pkg_t *providee = *provider++;
43 		abstract_pkg_t **dependers = providee->depended_upon_by;
44 		abstract_pkg_t *dep_ab_pkg;
45 		if (dependers == NULL)
46 			continue;
47 		while ((dep_ab_pkg = *dependers++) != NULL) {
48 			if (dep_ab_pkg->state_status == SS_INSTALLED) {
49 				n_installed_dependents++;
50 			}
51 		}
52 	}
53 
54 	/* if caller requested the set of installed dependents */
55 	if (pdependents) {
56 		int p = 0;
57 		abstract_pkg_t **dependents =
58 		    xcalloc((n_installed_dependents + 1),
59 			    sizeof(abstract_pkg_t *));
60 
61 		provider = provides;
62 		*pdependents = dependents;
63 
64 		while (provider && *provider) {
65 			abstract_pkg_t *providee = *provider++;
66 			abstract_pkg_t **dependers = providee->depended_upon_by;
67 			abstract_pkg_t *dep_ab_pkg;
68 			if (dependers == NULL)
69 				continue;
70 			while ((dep_ab_pkg = *dependers++) != NULL) {
71 				if (dep_ab_pkg->state_status == SS_INSTALLED
72 				    && !(dep_ab_pkg->state_flag & SF_MARKED)) {
73 					dependents[p++] = dep_ab_pkg;
74 					dep_ab_pkg->state_flag |= SF_MARKED;
75 				}
76 			}
77 		}
78 		dependents[p] = NULL;
79 		/* now clear the marks */
80 		for (i = 0; i < p; i++) {
81 			abstract_pkg_t *dep_ab_pkg = dependents[i];
82 			dep_ab_pkg->state_flag &= ~SF_MARKED;
83 		}
84 	}
85 	return n_installed_dependents;
86 }
87 
opkg_remove_dependent_pkgs(pkg_t * pkg,abstract_pkg_t ** dependents)88 static int opkg_remove_dependent_pkgs(pkg_t * pkg, abstract_pkg_t ** dependents)
89 {
90 	int i;
91 	int a;
92 	int count;
93 	pkg_vec_t *dependent_pkgs;
94 	abstract_pkg_t *ab_pkg;
95 
96 	if ((ab_pkg = pkg->parent) == NULL) {
97 		opkg_msg(ERROR, "Internal error: pkg %s isn't in hash table\n",
98 			 pkg->name);
99 		return 0;
100 	}
101 
102 	if (dependents == NULL)
103 		return 0;
104 
105 	// here i am using the dependencies_checked
106 	if (ab_pkg->dependencies_checked == 2)	// variable to make out whether this package
107 		return 0;	// has already been encountered in the process
108 	// of marking packages for removal - Karthik
109 	ab_pkg->dependencies_checked = 2;
110 
111 	i = 0;
112 	count = 1;
113 	dependent_pkgs = pkg_vec_alloc();
114 
115 	while (dependents[i] != NULL) {
116 		abstract_pkg_t *dep_ab_pkg = dependents[i];
117 
118 		if (dep_ab_pkg->dependencies_checked == 2) {
119 			i++;
120 			continue;
121 		}
122 		if (dep_ab_pkg->state_status == SS_INSTALLED) {
123 			for (a = 0; a < dep_ab_pkg->pkgs->len; a++) {
124 				pkg_t *dep_pkg = dep_ab_pkg->pkgs->pkgs[a];
125 				if (dep_pkg->state_status == SS_INSTALLED) {
126 					pkg_vec_insert(dependent_pkgs, dep_pkg);
127 					count++;
128 				}
129 			}
130 		}
131 		i++;
132 		/* 1 - to keep track of visited ab_pkgs when checking for possiblility of a broken removal of pkgs.
133 		 * 2 - to keep track of pkgs whose deps have been checked alrdy  - Karthik */
134 	}
135 
136 	if (count == 1) {
137 		pkg_vec_free(dependent_pkgs);
138 		return 0;
139 	}
140 
141 	int err = 0;
142 	for (i = 0; i < dependent_pkgs->len; i++) {
143 		err = opkg_remove_pkg(dependent_pkgs->pkgs[i], 0);
144 		if (err) {
145 			break;
146 		}
147 	}
148 	pkg_vec_free(dependent_pkgs);
149 	return err;
150 }
151 
print_dependents_warning(pkg_t * pkg,abstract_pkg_t ** dependents)152 static void print_dependents_warning(pkg_t * pkg, abstract_pkg_t ** dependents)
153 {
154 	abstract_pkg_t *dep_ab_pkg;
155 	opkg_msg(ERROR, "Package %s is depended upon by packages:\n",
156 		 pkg->name);
157 	while ((dep_ab_pkg = *dependents++) != NULL) {
158 		if (dep_ab_pkg->state_status == SS_INSTALLED)
159 			opkg_msg(ERROR, "\t%s\n", dep_ab_pkg->name);
160 	}
161 	opkg_msg(ERROR,
162 		 "These might cease to work if package %s is removed.\n\n",
163 		 pkg->name);
164 	opkg_msg(ERROR,
165 		 "Force removal of this package with --force-depends.\n");
166 	opkg_msg(ERROR, "Force removal of this package and its dependents\n");
167 	opkg_msg(ERROR, "with --force-removal-of-dependent-packages.\n");
168 }
169 
170 /*
171  * Find and remove packages that were autoinstalled and are orphaned
172  * by the removal of pkg.
173  */
remove_autoinstalled(pkg_t * pkg)174 static int remove_autoinstalled(pkg_t * pkg)
175 {
176 	int j;
177 	int err = 0;
178 	int n_deps;
179 	pkg_t *p;
180 	struct compound_depend *cdep;
181 	abstract_pkg_t **dependents;
182 
183 	for (cdep = pkg_get_ptr(pkg, PKG_DEPENDS); cdep && cdep->type; cdep++) {
184 		if (cdep->type != PREDEPEND
185 		    && cdep->type != DEPEND && cdep->type != RECOMMEND)
186 			continue;
187 		for (j = 0; j < cdep->possibility_count; j++) {
188 			p = pkg_hash_fetch_installed_by_name(cdep->
189 							     possibilities[j]->
190 							     pkg->name);
191 
192 			/* If the package is not installed, this could have
193 			 * been a circular dependency and the package has
194 			 * already been removed.
195 			 */
196 			if (!p)
197 				return -1;
198 
199 			if (!p->auto_installed)
200 				continue;
201 
202 			n_deps = pkg_has_installed_dependents(p, &dependents);
203 			if (n_deps == 0) {
204 				opkg_msg(NOTICE, "%s was autoinstalled and is "
205 					 "now orphaned, removing.\n", p->name);
206 				if (opkg_remove_pkg(p, 0) != 0) {
207 					err = -1;
208 				}
209 			} else
210 				opkg_msg(INFO, "%s was autoinstalled and is "
211 					 "still required by %d "
212 					 "installed packages.\n",
213 					 p->name, n_deps);
214 
215 			if (dependents)
216 				free(dependents);
217 		}
218 	}
219 
220 	return err;
221 }
222 
opkg_remove_pkg(pkg_t * pkg,int from_upgrade)223 int opkg_remove_pkg(pkg_t * pkg, int from_upgrade)
224 {
225 	int err;
226 	abstract_pkg_t *parent_pkg = NULL;
227 
228 /*
229  * If called from an upgrade and not from a normal remove,
230  * ignore the essential flag.
231  */
232 	if (pkg->essential && !from_upgrade) {
233 		if (conf->force_removal_of_essential_packages) {
234 			opkg_msg(NOTICE,
235 				 "Removing essential package %s under your coercion.\n"
236 				 "\tIf your system breaks, you get to keep both pieces\n",
237 				 pkg->name);
238 		} else {
239 			opkg_msg(NOTICE,
240 				 "Refusing to remove essential package %s.\n"
241 				 "\tRemoving an essential package may lead to an unusable system, but if\n"
242 				 "\tyou enjoy that kind of pain, you can force opkg to proceed against\n"
243 				 "\tits will with the option: --force-removal-of-essential-packages\n",
244 				 pkg->name);
245 			return -1;
246 		}
247 	}
248 
249 	if ((parent_pkg = pkg->parent) == NULL)
250 		return 0;
251 
252 	/* only attempt to remove dependent installed packages if
253 	 * force_depends is not specified or the package is being
254 	 * replaced.
255 	 */
256 	if (!conf->force_depends && !(pkg->state_flag & SF_REPLACE)) {
257 		abstract_pkg_t **dependents;
258 		int has_installed_dependents =
259 		    pkg_has_installed_dependents(pkg, &dependents);
260 
261 		if (has_installed_dependents) {
262 			/*
263 			 * if this package is depended upon by others, then either we should
264 			 * not remove it or we should remove it and all of its dependents
265 			 */
266 
267 			if (!conf->force_removal_of_dependent_packages) {
268 				print_dependents_warning(pkg, dependents);
269 				free(dependents);
270 				return -1;
271 			}
272 
273 			/* remove packages depending on this package - Karthik */
274 			err = opkg_remove_dependent_pkgs(pkg, dependents);
275 			if (err) {
276 				free(dependents);
277 				return err;
278 			}
279 		}
280 		if (dependents)
281 			free(dependents);
282 	}
283 
284 	if (from_upgrade == 0) {
285 		opkg_msg(NOTICE, "Removing package %s from %s...\n",
286 			 pkg->name, pkg->dest->name);
287 	}
288 	pkg->state_flag |= SF_FILELIST_CHANGED;
289 
290 	pkg->state_want = SW_DEINSTALL;
291 	opkg_state_changed++;
292 
293 	if (pkg_run_script(pkg, "prerm", "remove") != 0) {
294 		if (!conf->force_remove) {
295 			opkg_msg(ERROR, "not removing package \"%s\", "
296 				 "prerm script failed\n", pkg->name);
297 			opkg_msg(NOTICE,
298 				 "You can force removal of packages with failed "
299 				 "prerm scripts with the option: \n"
300 				 "\t--force-remove\n");
301 			return -1;
302 		}
303 	}
304 
305 	/* DPKG_INCOMPATIBILITY: dpkg is slightly different here. It
306 	   maintains an empty filelist rather than deleting it. That seems
307 	   like a big pain, and I don't see that that should make a big
308 	   difference, but for anyone who wants tighter compatibility,
309 	   feel free to fix this. */
310 	remove_data_files_and_list(pkg);
311 
312 	err = pkg_run_script(pkg, "postrm", "remove");
313 
314 	remove_maintainer_scripts(pkg);
315 	pkg->state_status = SS_NOT_INSTALLED;
316 	pkg_alternatives_update(pkg);
317 
318 	if (parent_pkg)
319 		parent_pkg->state_status = SS_NOT_INSTALLED;
320 
321 	/* remove autoinstalled packages that are orphaned by the removal of this one */
322 	if (conf->autoremove) {
323 		if (remove_autoinstalled(pkg) != 0) {
324 			err = -1;
325 		}
326 	}
327 	return err;
328 }
329 
remove_data_files_and_list(pkg_t * pkg)330 void remove_data_files_and_list(pkg_t * pkg)
331 {
332 	str_list_t installed_dirs;
333 	str_list_t *installed_files;
334 	str_list_elt_t *iter;
335 	char *file_name;
336 	conffile_t *conffile;
337 	int removed_a_dir;
338 	pkg_t *owner;
339 	int rootdirlen = 0;
340 
341 	installed_files = pkg_get_installed_files(pkg);
342 	if (installed_files == NULL) {
343 		opkg_msg(ERROR, "Failed to determine installed "
344 			 "files for %s. None removed.\n", pkg->name);
345 		return;
346 	}
347 
348 	str_list_init(&installed_dirs);
349 
350 	/* don't include trailing slash */
351 	if (conf->offline_root)
352 		rootdirlen = strlen(conf->offline_root);
353 
354 	for (iter = str_list_first(installed_files); iter;
355 	     iter = str_list_next(installed_files, iter)) {
356 		file_name = (char *)iter->data;
357 
358 		owner = file_hash_get_file_owner(file_name);
359 		if (owner != pkg)
360 			/* File may have been claimed by another package. */
361 			continue;
362 
363 		if (file_is_dir(file_name)) {
364 			str_list_append(&installed_dirs, file_name);
365 			continue;
366 		}
367 
368 		conffile = pkg_get_conffile(pkg, file_name + rootdirlen);
369 		if (conffile) {
370 			if (conffile_has_been_modified(conffile)) {
371 				opkg_msg(NOTICE,
372 					 "Not deleting modified conffile %s.\n",
373 					 file_name);
374 				continue;
375 			}
376 		}
377 
378 		if (!conf->noaction) {
379 			opkg_msg(INFO, "Deleting %s.\n", file_name);
380 			unlink(file_name);
381 		} else
382 			opkg_msg(INFO, "Not deleting %s. (noaction)\n",
383 				 file_name);
384 
385 		file_hash_remove(file_name);
386 	}
387 
388 	/* Remove empty directories */
389 	if (!conf->noaction) {
390 		do {
391 			removed_a_dir = 0;
392 			for (iter = str_list_first(&installed_dirs); iter;
393 			     iter = str_list_next(&installed_dirs, iter)) {
394 				file_name = (char *)iter->data;
395 
396 				if (rmdir(file_name) == 0) {
397 					opkg_msg(INFO, "Deleting %s.\n",
398 						 file_name);
399 					removed_a_dir = 1;
400 					str_list_remove(&installed_dirs, &iter);
401 				}
402 			}
403 		} while (removed_a_dir);
404 	}
405 
406 	pkg_free_installed_files(pkg);
407 	pkg_remove_installed_files_list(pkg);
408 
409 	/* Don't print warning for dirs that are provided by other packages */
410 	for (iter = str_list_first(&installed_dirs); iter;
411 	     iter = str_list_next(&installed_dirs, iter)) {
412 		file_name = (char *)iter->data;
413 
414 		owner = file_hash_get_file_owner(file_name);
415 		if (owner) {
416 			free(iter->data);
417 			iter->data = NULL;
418 			str_list_remove(&installed_dirs, &iter);
419 		}
420 	}
421 
422 	/* cleanup */
423 	while (!void_list_empty(&installed_dirs)) {
424 		iter = str_list_pop(&installed_dirs);
425 		free(iter->data);
426 		free(iter);
427 	}
428 	str_list_deinit(&installed_dirs);
429 }
430 
remove_maintainer_scripts(pkg_t * pkg)431 void remove_maintainer_scripts(pkg_t * pkg)
432 {
433 	int i, err;
434 	char *globpattern;
435 	glob_t globbuf;
436 
437 	if (conf->noaction)
438 		return;
439 
440 	sprintf_alloc(&globpattern, "%s/%s.*", pkg->dest->info_dir, pkg->name);
441 
442 	err = glob(globpattern, 0, NULL, &globbuf);
443 	free(globpattern);
444 	if (err)
445 		return;
446 
447 	for (i = 0; i < globbuf.gl_pathc; i++) {
448 		opkg_msg(INFO, "Deleting %s.\n", globbuf.gl_pathv[i]);
449 		unlink(globbuf.gl_pathv[i]);
450 	}
451 	globfree(&globbuf);
452 }
453