unarchive: fix possible segmentation fault in deb_extract()
[oweals/opkg-lede.git] / libopkg / opkg_remove.c
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 "file_util.h"
26 #include "sprintf_alloc.h"
27 #include "libbb/libbb.h"
28
29 /*
30  * Returns number of the number of packages depending on the packages provided by this package.
31  * Every package implicitly provides itself.
32  */
33 int pkg_has_installed_dependents(pkg_t * pkg, abstract_pkg_t *** pdependents)
34 {
35         abstract_pkg_t **provider, **provides = pkg_get_ptr(pkg, PKG_PROVIDES);
36         unsigned int i, n_installed_dependents = 0;
37
38         provider = provides;
39
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)
45                         continue;
46                 while ((dep_ab_pkg = *dependers++) != NULL) {
47                         if (dep_ab_pkg->state_status == SS_INSTALLED) {
48                                 n_installed_dependents++;
49                         }
50                 }
51         }
52
53         /* if caller requested the set of installed dependents */
54         if (pdependents) {
55                 int p = 0;
56                 abstract_pkg_t **dependents =
57                     xcalloc((n_installed_dependents + 1),
58                             sizeof(abstract_pkg_t *));
59
60                 provider = provides;
61                 *pdependents = dependents;
62
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)
68                                 continue;
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;
74                                 }
75                         }
76                 }
77                 dependents[p] = NULL;
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;
82                 }
83         }
84         return n_installed_dependents;
85 }
86
87 static int opkg_remove_dependent_pkgs(pkg_t * pkg, abstract_pkg_t ** dependents)
88 {
89         int i;
90         int a;
91         int count;
92         pkg_vec_t *dependent_pkgs;
93         abstract_pkg_t *ab_pkg;
94
95         if ((ab_pkg = pkg->parent) == NULL) {
96                 opkg_msg(ERROR, "Internal error: pkg %s isn't in hash table\n",
97                          pkg->name);
98                 return 0;
99         }
100
101         if (dependents == NULL)
102                 return 0;
103
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;
109
110         i = 0;
111         count = 1;
112         dependent_pkgs = pkg_vec_alloc();
113
114         while (dependents[i] != NULL) {
115                 abstract_pkg_t *dep_ab_pkg = dependents[i];
116
117                 if (dep_ab_pkg->dependencies_checked == 2) {
118                         i++;
119                         continue;
120                 }
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);
126                                         count++;
127                                 }
128                         }
129                 }
130                 i++;
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 */
133         }
134
135         if (count == 1) {
136                 pkg_vec_free(dependent_pkgs);
137                 return 0;
138         }
139
140         int err = 0;
141         for (i = 0; i < dependent_pkgs->len; i++) {
142                 err = opkg_remove_pkg(dependent_pkgs->pkgs[i], 0);
143                 if (err) {
144                         break;
145                 }
146         }
147         pkg_vec_free(dependent_pkgs);
148         return err;
149 }
150
151 static void print_dependents_warning(pkg_t * pkg, abstract_pkg_t ** dependents)
152 {
153         abstract_pkg_t *dep_ab_pkg;
154         opkg_msg(ERROR, "Package %s is depended upon by packages:\n",
155                  pkg->name);
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);
159         }
160         opkg_msg(ERROR,
161                  "These might cease to work if package %s is removed.\n\n",
162                  pkg->name);
163         opkg_msg(ERROR,
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");
167 }
168
169 /*
170  * Find and remove packages that were autoinstalled and are orphaned
171  * by the removal of pkg.
172  */
173 static int remove_autoinstalled(pkg_t * pkg)
174 {
175         int j;
176         int err = 0;
177         int n_deps;
178         pkg_t *p;
179         struct compound_depend *cdep;
180         abstract_pkg_t **dependents;
181
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)
185                         continue;
186                 for (j = 0; j < cdep->possibility_count; j++) {
187                         p = pkg_hash_fetch_installed_by_name(cdep->
188                                                              possibilities[j]->
189                                                              pkg->name);
190
191                         /* If the package is not installed, this could have
192                          * been a circular dependency and the package has
193                          * already been removed.
194                          */
195                         if (!p)
196                                 return -1;
197
198                         if (!p->auto_installed)
199                                 continue;
200
201                         n_deps = pkg_has_installed_dependents(p, &dependents);
202                         if (n_deps == 0) {
203                                 opkg_msg(NOTICE, "%s was autoinstalled and is "
204                                          "now orphaned, removing.\n", p->name);
205                                 if (opkg_remove_pkg(p, 0) != 0) {
206                                         err = -1;
207                                 }
208                         } else
209                                 opkg_msg(INFO, "%s was autoinstalled and is "
210                                          "still required by %d "
211                                          "installed packages.\n",
212                                          p->name, n_deps);
213
214                         if (dependents)
215                                 free(dependents);
216                 }
217         }
218
219         return err;
220 }
221
222 int opkg_remove_pkg(pkg_t * pkg, int from_upgrade)
223 {
224         int err;
225         abstract_pkg_t *parent_pkg = NULL;
226
227 /*
228  * If called from an upgrade and not from a normal remove,
229  * ignore the essential flag.
230  */
231         if (pkg->essential && !from_upgrade) {
232                 if (conf->force_removal_of_essential_packages) {
233                         opkg_msg(NOTICE,
234                                  "Removing essential package %s under your coercion.\n"
235                                  "\tIf your system breaks, you get to keep both pieces\n",
236                                  pkg->name);
237                 } else {
238                         opkg_msg(NOTICE,
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",
243                                  pkg->name);
244                         return -1;
245                 }
246         }
247
248         if ((parent_pkg = pkg->parent) == NULL)
249                 return 0;
250
251         /* only attempt to remove dependent installed packages if
252          * force_depends is not specified or the package is being
253          * replaced.
254          */
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);
259
260                 if (has_installed_dependents) {
261                         /*
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
264                          */
265
266                         if (!conf->force_removal_of_dependent_packages) {
267                                 print_dependents_warning(pkg, dependents);
268                                 free(dependents);
269                                 return -1;
270                         }
271
272                         /* remove packages depending on this package - Karthik */
273                         err = opkg_remove_dependent_pkgs(pkg, dependents);
274                         if (err) {
275                                 free(dependents);
276                                 return err;
277                         }
278                 }
279                 if (dependents)
280                         free(dependents);
281         }
282
283         if (from_upgrade == 0) {
284                 opkg_msg(NOTICE, "Removing package %s from %s...\n",
285                          pkg->name, pkg->dest->name);
286         }
287         pkg->state_flag |= SF_FILELIST_CHANGED;
288
289         pkg->state_want = SW_DEINSTALL;
290         opkg_state_changed++;
291
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);
296                         opkg_msg(NOTICE,
297                                  "You can force removal of packages with failed "
298                                  "prerm scripts with the option: \n"
299                                  "\t--force-remove\n");
300                         return -1;
301                 }
302         }
303
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);
310
311         err = pkg_run_script(pkg, "postrm", "remove");
312
313         remove_maintainer_scripts(pkg);
314         pkg->state_status = SS_NOT_INSTALLED;
315
316         if (parent_pkg)
317                 parent_pkg->state_status = SS_NOT_INSTALLED;
318
319         /* remove autoinstalled packages that are orphaned by the removal of this one */
320         if (conf->autoremove) {
321                 if (remove_autoinstalled(pkg) != 0) {
322                         err = -1;
323                 }
324         }
325         return err;
326 }
327
328 void remove_data_files_and_list(pkg_t * pkg)
329 {
330         str_list_t installed_dirs;
331         str_list_t *installed_files;
332         str_list_elt_t *iter;
333         char *file_name;
334         conffile_t *conffile;
335         int removed_a_dir;
336         pkg_t *owner;
337         int rootdirlen = 0;
338
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);
343                 return;
344         }
345
346         str_list_init(&installed_dirs);
347
348         /* don't include trailing slash */
349         if (conf->offline_root)
350                 rootdirlen = strlen(conf->offline_root);
351
352         for (iter = str_list_first(installed_files); iter;
353              iter = str_list_next(installed_files, iter)) {
354                 file_name = (char *)iter->data;
355
356                 owner = file_hash_get_file_owner(file_name);
357                 if (owner != pkg)
358                         /* File may have been claimed by another package. */
359                         continue;
360
361                 if (file_is_dir(file_name)) {
362                         str_list_append(&installed_dirs, file_name);
363                         continue;
364                 }
365
366                 conffile = pkg_get_conffile(pkg, file_name + rootdirlen);
367                 if (conffile) {
368                         if (conffile_has_been_modified(conffile)) {
369                                 opkg_msg(NOTICE,
370                                          "Not deleting modified conffile %s.\n",
371                                          file_name);
372                                 continue;
373                         }
374                 }
375
376                 if (!conf->noaction) {
377                         opkg_msg(INFO, "Deleting %s.\n", file_name);
378                         unlink(file_name);
379                 } else
380                         opkg_msg(INFO, "Not deleting %s. (noaction)\n",
381                                  file_name);
382
383                 file_hash_remove(file_name);
384         }
385
386         /* Remove empty directories */
387         if (!conf->noaction) {
388                 do {
389                         removed_a_dir = 0;
390                         for (iter = str_list_first(&installed_dirs); iter;
391                              iter = str_list_next(&installed_dirs, iter)) {
392                                 file_name = (char *)iter->data;
393
394                                 if (rmdir(file_name) == 0) {
395                                         opkg_msg(INFO, "Deleting %s.\n",
396                                                  file_name);
397                                         removed_a_dir = 1;
398                                         str_list_remove(&installed_dirs, &iter);
399                                 }
400                         }
401                 } while (removed_a_dir);
402         }
403
404         pkg_free_installed_files(pkg);
405         pkg_remove_installed_files_list(pkg);
406
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;
411
412                 owner = file_hash_get_file_owner(file_name);
413                 if (owner) {
414                         free(iter->data);
415                         iter->data = NULL;
416                         str_list_remove(&installed_dirs, &iter);
417                 }
418         }
419
420         /* cleanup */
421         while (!void_list_empty(&installed_dirs)) {
422                 iter = str_list_pop(&installed_dirs);
423                 free(iter->data);
424                 free(iter);
425         }
426         str_list_deinit(&installed_dirs);
427 }
428
429 void remove_maintainer_scripts(pkg_t * pkg)
430 {
431         int i, err;
432         char *globpattern;
433         glob_t globbuf;
434
435         if (conf->noaction)
436                 return;
437
438         sprintf_alloc(&globpattern, "%s/%s.*", pkg->dest->info_dir, pkg->name);
439
440         err = glob(globpattern, 0, NULL, &globbuf);
441         free(globpattern);
442         if (err)
443                 return;
444
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]);
448         }
449         globfree(&globbuf);
450 }