pkg: use a blob buffer in pkg_t to store variable fields
[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         int nprovides = pkg->provides_count;
36         abstract_pkg_t **provides = pkg->provides;
37         unsigned int n_installed_dependents = 0;
38         int i;
39         for (i = 0; i < nprovides; i++) {
40                 abstract_pkg_t *providee = provides[i];
41                 abstract_pkg_t **dependers = providee->depended_upon_by;
42                 abstract_pkg_t *dep_ab_pkg;
43                 if (dependers == NULL)
44                         continue;
45                 while ((dep_ab_pkg = *dependers++) != NULL) {
46                         if (dep_ab_pkg->state_status == SS_INSTALLED) {
47                                 n_installed_dependents++;
48                         }
49                 }
50
51         }
52         /* if caller requested the set of installed dependents */
53         if (pdependents) {
54                 int p = 0;
55                 abstract_pkg_t **dependents =
56                     xcalloc((n_installed_dependents + 1),
57                             sizeof(abstract_pkg_t *));
58
59                 *pdependents = dependents;
60                 for (i = 0; i < nprovides; i++) {
61                         abstract_pkg_t *providee = provides[i];
62                         abstract_pkg_t **dependers = providee->depended_upon_by;
63                         abstract_pkg_t *dep_ab_pkg;
64                         if (dependers == NULL)
65                                 continue;
66                         while ((dep_ab_pkg = *dependers++) != NULL) {
67                                 if (dep_ab_pkg->state_status == SS_INSTALLED
68                                     && !(dep_ab_pkg->state_flag & SF_MARKED)) {
69                                         dependents[p++] = dep_ab_pkg;
70                                         dep_ab_pkg->state_flag |= SF_MARKED;
71                                 }
72                         }
73                 }
74                 dependents[p] = NULL;
75                 /* now clear the marks */
76                 for (i = 0; i < p; i++) {
77                         abstract_pkg_t *dep_ab_pkg = dependents[i];
78                         dep_ab_pkg->state_flag &= ~SF_MARKED;
79                 }
80         }
81         return n_installed_dependents;
82 }
83
84 static int opkg_remove_dependent_pkgs(pkg_t * pkg, abstract_pkg_t ** dependents)
85 {
86         int i;
87         int a;
88         int count;
89         pkg_vec_t *dependent_pkgs;
90         abstract_pkg_t *ab_pkg;
91
92         if ((ab_pkg = pkg->parent) == NULL) {
93                 opkg_msg(ERROR, "Internal error: pkg %s isn't in hash table\n",
94                          pkg->name);
95                 return 0;
96         }
97
98         if (dependents == NULL)
99                 return 0;
100
101         // here i am using the dependencies_checked
102         if (ab_pkg->dependencies_checked == 2)  // variable to make out whether this package
103                 return 0;       // has already been encountered in the process
104         // of marking packages for removal - Karthik
105         ab_pkg->dependencies_checked = 2;
106
107         i = 0;
108         count = 1;
109         dependent_pkgs = pkg_vec_alloc();
110
111         while (dependents[i] != NULL) {
112                 abstract_pkg_t *dep_ab_pkg = dependents[i];
113
114                 if (dep_ab_pkg->dependencies_checked == 2) {
115                         i++;
116                         continue;
117                 }
118                 if (dep_ab_pkg->state_status == SS_INSTALLED) {
119                         for (a = 0; a < dep_ab_pkg->pkgs->len; a++) {
120                                 pkg_t *dep_pkg = dep_ab_pkg->pkgs->pkgs[a];
121                                 if (dep_pkg->state_status == SS_INSTALLED) {
122                                         pkg_vec_insert(dependent_pkgs, dep_pkg);
123                                         count++;
124                                 }
125                         }
126                 }
127                 i++;
128                 /* 1 - to keep track of visited ab_pkgs when checking for possiblility of a broken removal of pkgs.
129                  * 2 - to keep track of pkgs whose deps have been checked alrdy  - Karthik */
130         }
131
132         if (count == 1) {
133                 pkg_vec_free(dependent_pkgs);
134                 return 0;
135         }
136
137         int err = 0;
138         for (i = 0; i < dependent_pkgs->len; i++) {
139                 err = opkg_remove_pkg(dependent_pkgs->pkgs[i], 0);
140                 if (err) {
141                         break;
142                 }
143         }
144         pkg_vec_free(dependent_pkgs);
145         return err;
146 }
147
148 static void print_dependents_warning(pkg_t * pkg, abstract_pkg_t ** dependents)
149 {
150         abstract_pkg_t *dep_ab_pkg;
151         opkg_msg(ERROR, "Package %s is depended upon by packages:\n",
152                  pkg->name);
153         while ((dep_ab_pkg = *dependents++) != NULL) {
154                 if (dep_ab_pkg->state_status == SS_INSTALLED)
155                         opkg_msg(ERROR, "\t%s\n", dep_ab_pkg->name);
156         }
157         opkg_msg(ERROR,
158                  "These might cease to work if package %s is removed.\n\n",
159                  pkg->name);
160         opkg_msg(ERROR,
161                  "Force removal of this package with --force-depends.\n");
162         opkg_msg(ERROR, "Force removal of this package and its dependents\n");
163         opkg_msg(ERROR, "with --force-removal-of-dependent-packages.\n");
164 }
165
166 /*
167  * Find and remove packages that were autoinstalled and are orphaned
168  * by the removal of pkg.
169  */
170 static int remove_autoinstalled(pkg_t * pkg)
171 {
172         int i, j;
173         int err = 0;
174         int n_deps;
175         pkg_t *p;
176         struct compound_depend *cdep;
177         abstract_pkg_t **dependents;
178
179         int count = pkg->pre_depends_count +
180             pkg->depends_count + pkg->recommends_count + pkg->suggests_count;
181
182         for (i = 0; i < count; i++) {
183                 cdep = &pkg->depends[i];
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
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
317         if (parent_pkg)
318                 parent_pkg->state_status = SS_NOT_INSTALLED;
319
320         /* remove autoinstalled packages that are orphaned by the removal of this one */
321         if (conf->autoremove) {
322                 if (remove_autoinstalled(pkg) != 0) {
323                         err = -1;
324                 }
325         }
326         return err;
327 }
328
329 void remove_data_files_and_list(pkg_t * pkg)
330 {
331         str_list_t installed_dirs;
332         str_list_t *installed_files;
333         str_list_elt_t *iter;
334         char *file_name;
335         conffile_t *conffile;
336         int removed_a_dir;
337         pkg_t *owner;
338         int rootdirlen = 0;
339
340         installed_files = pkg_get_installed_files(pkg);
341         if (installed_files == NULL) {
342                 opkg_msg(ERROR, "Failed to determine installed "
343                          "files for %s. None removed.\n", pkg->name);
344                 return;
345         }
346
347         str_list_init(&installed_dirs);
348
349         /* don't include trailing slash */
350         if (conf->offline_root)
351                 rootdirlen = strlen(conf->offline_root);
352
353         for (iter = str_list_first(installed_files); iter;
354              iter = str_list_next(installed_files, iter)) {
355                 file_name = (char *)iter->data;
356
357                 owner = file_hash_get_file_owner(file_name);
358                 if (owner != pkg)
359                         /* File may have been claimed by another package. */
360                         continue;
361
362                 if (file_is_dir(file_name)) {
363                         str_list_append(&installed_dirs, file_name);
364                         continue;
365                 }
366
367                 conffile = pkg_get_conffile(pkg, file_name + rootdirlen);
368                 if (conffile) {
369                         if (conffile_has_been_modified(conffile)) {
370                                 opkg_msg(NOTICE,
371                                          "Not deleting modified conffile %s.\n",
372                                          file_name);
373                                 continue;
374                         }
375                 }
376
377                 if (!conf->noaction) {
378                         opkg_msg(INFO, "Deleting %s.\n", file_name);
379                         unlink(file_name);
380                 } else
381                         opkg_msg(INFO, "Not deleting %s. (noaction)\n",
382                                  file_name);
383
384                 file_hash_remove(file_name);
385         }
386
387         /* Remove empty directories */
388         if (!conf->noaction) {
389                 do {
390                         removed_a_dir = 0;
391                         for (iter = str_list_first(&installed_dirs); iter;
392                              iter = str_list_next(&installed_dirs, iter)) {
393                                 file_name = (char *)iter->data;
394
395                                 if (rmdir(file_name) == 0) {
396                                         opkg_msg(INFO, "Deleting %s.\n",
397                                                  file_name);
398                                         removed_a_dir = 1;
399                                         str_list_remove(&installed_dirs, &iter);
400                                 }
401                         }
402                 } while (removed_a_dir);
403         }
404
405         pkg_free_installed_files(pkg);
406         pkg_remove_installed_files_list(pkg);
407
408         /* Don't print warning for dirs that are provided by other packages */
409         for (iter = str_list_first(&installed_dirs); iter;
410              iter = str_list_next(&installed_dirs, iter)) {
411                 file_name = (char *)iter->data;
412
413                 owner = file_hash_get_file_owner(file_name);
414                 if (owner) {
415                         free(iter->data);
416                         iter->data = NULL;
417                         str_list_remove(&installed_dirs, &iter);
418                 }
419         }
420
421         /* cleanup */
422         while (!void_list_empty(&installed_dirs)) {
423                 iter = str_list_pop(&installed_dirs);
424                 free(iter->data);
425                 free(iter);
426         }
427         str_list_deinit(&installed_dirs);
428 }
429
430 void remove_maintainer_scripts(pkg_t * pkg)
431 {
432         int i, err;
433         char *globpattern;
434         glob_t globbuf;
435
436         if (conf->noaction)
437                 return;
438
439         sprintf_alloc(&globpattern, "%s/%s.*", pkg->dest->info_dir, pkg->name);
440
441         err = glob(globpattern, 0, NULL, &globbuf);
442         free(globpattern);
443         if (err)
444                 return;
445
446         for (i = 0; i < globbuf.gl_pathc; i++) {
447                 opkg_msg(INFO, "Deleting %s.\n", globbuf.gl_pathv[i]);
448                 unlink(globbuf.gl_pathv[i]);
449         }
450         globfree(&globbuf);
451 }