opkg: add autoremove command line option
[oweals/opkg-lede.git] / libopkg / opkg_remove.c
1 /* opkg_remove.c - the itsy 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 "opkg.h"
19 #include "opkg_message.h"
20
21 #include <glob.h>
22
23 #include "opkg_remove.h"
24
25 #include "file_util.h"
26 #include "sprintf_alloc.h"
27 #include "str_util.h"
28
29 #include "opkg_cmd.h"
30
31 /*
32  * Returns number of the number of packages depending on the packages provided by this package.
33  * Every package implicitly provides itself.
34  */
35 int pkg_has_installed_dependents(opkg_conf_t *conf, abstract_pkg_t *parent_apkg, pkg_t *pkg, abstract_pkg_t *** pdependents)
36 {
37      int nprovides = pkg->provides_count;
38      abstract_pkg_t **provides = pkg->provides;
39      int n_installed_dependents = 0;
40      int i;
41      for (i = 0; i <= nprovides; i++) {
42           abstract_pkg_t *providee = provides[i];
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 = (abstract_pkg_t **)malloc((n_installed_dependents+1)*sizeof(abstract_pkg_t *));
58
59           if ( dependents == NULL ){
60               fprintf(stderr,"%s Unable to allocate memory. REPORT THIS BUG IN BUGZILLA PLEASE\n", __FUNCTION__);
61               return -1;  
62           }
63
64           *pdependents = dependents;
65           for (i = 0; i <= nprovides; i++) {
66                abstract_pkg_t *providee = provides[i];
67                abstract_pkg_t **dependers = providee->depended_upon_by;
68                abstract_pkg_t *dep_ab_pkg;
69                if (dependers == NULL)
70                     continue;
71                while ((dep_ab_pkg = *dependers++) != NULL) {
72                     if (dep_ab_pkg->state_status == SS_INSTALLED && !(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
88 int opkg_remove_dependent_pkgs (opkg_conf_t *conf, pkg_t *pkg, abstract_pkg_t **dependents)
89 {
90     int i;
91     int a;
92     int count;
93     pkg_vec_t *dependent_pkgs = pkg_vec_alloc();
94     abstract_pkg_t * ab_pkg;
95
96     if((ab_pkg = pkg->parent) == NULL){
97         fprintf(stderr, "%s: unable to get dependent pkgs. pkg %s isn't in hash table\n",
98                 __FUNCTION__, 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     while (dependents [i] != NULL) {
114         abstract_pkg_t *dep_ab_pkg = dependents[i];
115         
116         if (dep_ab_pkg->dependencies_checked == 2){
117             i++;
118             continue;   
119         }
120         if (dep_ab_pkg->state_status == SS_INSTALLED) {
121             for (a = 0; a < dep_ab_pkg->pkgs->len; a++) {
122                 pkg_t *dep_pkg = dep_ab_pkg->pkgs->pkgs[a];
123                 if (dep_pkg->state_status == SS_INSTALLED) {
124                     pkg_vec_insert(dependent_pkgs, dep_pkg);
125                     count++;
126                 }
127             }
128         }
129         i++;
130         /* 1 - to keep track of visited ab_pkgs when checking for possiblility of a broken removal of pkgs.
131          * 2 - to keep track of pkgs whose deps have been checked alrdy  - Karthik */   
132     }
133     
134     if (count == 1)
135             return 0;
136     
137     
138     for (i = 0; i < dependent_pkgs->len; i++) {
139         int err = opkg_remove_pkg(conf, dependent_pkgs->pkgs[i],0);
140         if (err)
141             return err;
142     }
143     return 0;
144 }
145
146 static int user_prefers_removing_dependents(opkg_conf_t *conf, abstract_pkg_t *abpkg, pkg_t *pkg, abstract_pkg_t **dependents)
147 {
148     abstract_pkg_t *dep_ab_pkg;
149     opkg_message(conf, OPKG_ERROR, "Package %s is depended upon by packages:\n", pkg->name);
150     while ((dep_ab_pkg = *dependents++) != NULL) {
151          if (dep_ab_pkg->state_status == SS_INSTALLED)
152               opkg_message(conf, OPKG_ERROR, "\t%s\n", dep_ab_pkg->name);
153     }
154     opkg_message(conf, OPKG_ERROR, "These might cease to work if package %s is removed.\n\n", pkg->name);
155     opkg_message(conf, OPKG_ERROR, "");
156     opkg_message(conf, OPKG_ERROR, "You can force removal of this package with -force-depends.\n");
157     opkg_message(conf, OPKG_ERROR, "You can force removal of this package and its dependents\n");
158     opkg_message(conf, OPKG_ERROR, "with -force-removal-of-dependent-packages or -recursive\n");
159     opkg_message(conf, OPKG_ERROR, "or by setting option force_removal_of_dependent_packages\n");
160     opkg_message(conf, OPKG_ERROR, "in opkg.conf.\n");
161     return 0;
162 }
163
164 int opkg_remove_pkg(opkg_conf_t *conf, pkg_t *pkg,int message)
165 {
166 /* Actually, when "message == 1" I have been called from an upgrade, and not from a normal remove
167    thus I wan't check for essential, as I'm upgrading.
168    I hope it won't break anything :) 
169 */
170      int err;
171      abstract_pkg_t *parent_pkg = NULL;
172
173      if (conf->autoremove)
174        printf ("autoremove is enabled, but not yet implemented\n");
175
176      if (pkg->essential && !message) {
177           if (conf->force_removal_of_essential_packages) {
178                fprintf(stderr, "WARNING: Removing essential package %s under your coercion.\n"
179                        "\tIf your system breaks, you get to keep both pieces\n",
180                        pkg->name);
181           } else {
182                fprintf(stderr, "ERROR: Refusing to remove essential package %s.\n"
183                        "\tRemoving an essential package may lead to an unusable system, but if\n"
184                        "\tyou enjoy that kind of pain, you can force opkg to proceed against\n"
185                        "\tits will with the option: -force-removal-of-essential-packages\n",
186                        pkg->name);
187                return OPKG_PKG_IS_ESSENTIAL;
188           }
189      }
190
191      if ((parent_pkg = pkg->parent) == NULL)
192           return 0;
193
194      /* only attempt to remove dependent installed packages if
195       * force_depends is not specified or the package is being
196       * replaced.
197       */
198      if (!conf->force_depends
199          && !(pkg->state_flag & SF_REPLACE)) {
200           abstract_pkg_t **dependents;
201           int has_installed_dependents = 
202                pkg_has_installed_dependents(conf, parent_pkg, pkg, &dependents);
203
204           if (has_installed_dependents) {
205                /*
206                 * if this package is depended up by others, then either we should
207                 * not remove it or we should remove it and all of its dependents 
208                 */
209
210                if (!conf->force_removal_of_dependent_packages
211                    && !user_prefers_removing_dependents(conf, parent_pkg, pkg, dependents)) {
212                     return OPKG_PKG_HAS_DEPENDENTS;
213                }
214
215                /* remove packages depending on this package - Karthik */
216                err = opkg_remove_dependent_pkgs (conf, pkg, dependents);
217                free(dependents);
218                if (err) return err;
219           }
220      }
221
222      if ( message==0 ){
223          printf("Removing package %s from %s...\n", pkg->name, pkg->dest->name);
224          fflush(stdout);
225      }
226      pkg->state_flag |= SF_FILELIST_CHANGED;
227
228      pkg->state_want = SW_DEINSTALL;
229      opkg_state_changed++;
230
231      pkg_run_script(conf, pkg, "prerm", "remove");
232
233      /* DPKG_INCOMPATIBILITY: dpkg is slightly different here. It
234         maintains an empty filelist rather than deleting it. That seems
235         like a big pain, and I don't see that that should make a big
236         difference, but for anyone who wants tighter compatibility,
237         feel free to fix this. */
238      remove_data_files_and_list(conf, pkg);
239
240      pkg_run_script(conf, pkg, "postrm", "remove");
241
242      remove_maintainer_scripts_except_postrm(conf, pkg);
243
244      /* Aman Gupta - Since opkg is made for handheld devices with limited
245       * space, it doesn't make sense to leave extra configurations, files, 
246       * and maintainer scripts left around. So, we make remove like purge, 
247       * and take out all the crap :) */
248
249      remove_postrm(conf, pkg);
250      pkg->state_status = SS_NOT_INSTALLED;
251
252      if (parent_pkg) 
253           parent_pkg->state_status = SS_NOT_INSTALLED;
254
255      return 0;
256 }
257
258 int opkg_purge_pkg(opkg_conf_t *conf, pkg_t *pkg)
259 {
260     opkg_remove_pkg(conf, pkg,0);
261     return 0;
262 }
263
264 int remove_data_files_and_list(opkg_conf_t *conf, pkg_t *pkg)
265 {
266      str_list_t installed_dirs;
267      str_list_t *installed_files;
268      str_list_elt_t *iter;
269      char *file_name;
270      conffile_t *conffile;
271      int removed_a_dir;
272      pkg_t *owner;
273
274      str_list_init(&installed_dirs);
275      installed_files = pkg_get_installed_files(pkg);
276
277      for (iter = installed_files->head; iter; iter = iter->next) {
278           file_name = iter->data;
279
280           if (file_is_dir(file_name)) {
281                str_list_append(&installed_dirs, strdup(file_name));
282                continue;
283           }
284
285           conffile = pkg_get_conffile(pkg, file_name);
286           if (conffile) {
287                /* XXX: QUESTION: Is this right? I figure we only need to
288                   save the conffile if it has been modified. Is that what
289                   dpkg does? Or does dpkg preserve all conffiles? If so,
290                   this seems like a better thing to do to conserve
291                   space. */
292                if (conffile_has_been_modified(conf, conffile)) {
293                     printf("  not deleting modified conffile %s\n", file_name);
294                     fflush(stdout);
295                     continue;
296                }
297           }
298
299           opkg_message(conf, OPKG_INFO, "  deleting %s (noaction=%d)\n", file_name, conf->noaction);
300           if (!conf->noaction)
301                unlink(file_name);
302      }
303
304      if (!conf->noaction) {
305           do {
306                removed_a_dir = 0;
307                for (iter = installed_dirs.head; iter; iter = iter->next) {
308                     file_name = iter->data;
309             
310                     if (rmdir(file_name) == 0) {
311                          opkg_message(conf, OPKG_INFO, "  deleting %s\n", file_name);
312                          removed_a_dir = 1;
313                          str_list_remove(&installed_dirs, &iter);
314                     }
315                }
316           } while (removed_a_dir);
317      }
318
319      pkg_free_installed_files(pkg);
320      /* We have to remove the file list now, so that
321         find_pkg_owning_file does not always just report this package */
322      pkg_remove_installed_files_list(conf, pkg);
323
324      /* Don't print warning for dirs that are provided by other packages */
325      for (iter = installed_dirs.head; iter; iter = iter->next) {
326           file_name = iter->data;
327
328           owner = file_hash_get_file_owner(conf, file_name);
329           if (owner) {
330                free(iter->data);
331                iter->data = NULL;
332                str_list_remove(&installed_dirs, &iter);
333           }
334      }
335
336      /* cleanup */
337      for (iter = installed_dirs.head; iter; iter = iter->next) {
338           free(iter->data);
339           iter->data = NULL;
340      }
341      str_list_deinit(&installed_dirs);
342
343      return 0;
344 }
345
346 int remove_maintainer_scripts_except_postrm(opkg_conf_t *conf, pkg_t *pkg)
347 {
348     int i, err;
349     char *globpattern;
350     glob_t globbuf;
351     
352     if (conf->noaction) return 0;
353
354     sprintf_alloc(&globpattern, "%s/%s.*",
355                   pkg->dest->info_dir, pkg->name);
356     err = glob(globpattern, 0, NULL, &globbuf);
357     free(globpattern);
358     if (err) {
359         return 0;
360     }
361
362     for (i = 0; i < globbuf.gl_pathc; i++) {
363         if (str_ends_with(globbuf.gl_pathv[i], ".postrm")) {
364             continue;
365         }
366         opkg_message(conf, OPKG_INFO, "  deleting %s\n", globbuf.gl_pathv[i]);
367         unlink(globbuf.gl_pathv[i]);
368     }
369     globfree(&globbuf);
370
371     return 0;
372 }
373
374 int remove_postrm(opkg_conf_t *conf, pkg_t *pkg)
375 {
376     char *postrm_file_name;
377
378     if (conf->noaction) return 0;
379
380     sprintf_alloc(&postrm_file_name, "%s/%s.postrm",
381                   pkg->dest->info_dir, pkg->name);
382     unlink(postrm_file_name);
383     free(postrm_file_name);
384
385     return 0;
386 }