/ src / unregister.c
unregister.c
  1  /*-
  2   * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
  3   * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
  4   * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
  5   * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
  6   * Copyright (c) 2016 Vsevolod Stakhov <vsevolod@FreeBSD.org>
  7   * All rights reserved.
  8   * Copyright (c) 2025 Emmanuel Vadot <manu@FreeBSD.org>
  9   *
 10   * Redistribution and use in source and binary forms, with or without
 11   * modification, are permitted provided that the following conditions
 12   * are met:
 13   * 1. Redistributions of source code must retain the above copyright
 14   *    notice, this list of conditions and the following disclaimer
 15   *    in this position and unchanged.
 16   * 2. Redistributions in binary form must reproduce the above copyright
 17   *    notice, this list of conditions and the following disclaimer in the
 18   *    documentation and/or other materials provided with the distribution.
 19   *
 20   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 21   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 22   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 23   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 24   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 25   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 26   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 27   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 28   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 29   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30   */
 31  
 32  #include <err.h>
 33  #include <getopt.h>
 34  #include <stdio.h>
 35  #include <string.h>
 36  #include <unistd.h>
 37  
 38  #include <pkg.h>
 39  
 40  #include "pkgcli.h"
 41  
 42  void
 43  usage_unregister(void)
 44  {
 45  	fprintf(stderr, "Usage: pkg unregister [-fnqRy] [-Cgix] <pkg-name> ...\n");
 46  	fprintf(stderr, "       pkg unregister [-nqy] -a\n\n");
 47  	fprintf(stderr, "For more information see 'pkg help unregister'.\n");
 48  }
 49  
 50  int
 51  exec_unregister(int argc, char **argv)
 52  {
 53  	struct pkg_jobs	*jobs = NULL;
 54  	struct pkgdb	*db = NULL;
 55  	match_t		 match = MATCH_EXACT;
 56  	pkg_flags	 f = PKG_FLAG_KEEPFILES | PKG_FLAG_NOSCRIPT;
 57  	bool		 recursive_flag = false, rc = false;
 58  	int		 retcode = EXIT_FAILURE;
 59  	int		 ch;
 60  	int		 i;
 61  	int		 lock_type = PKGDB_LOCK_ADVISORY;
 62  	int		 locked_pkgs = 0;
 63  	int		 nbactions = 0;
 64  
 65  	struct option longopts[] = {
 66  		{ "all",			no_argument,	NULL,	'a' },
 67  		{ "case-sensitive",		no_argument,	NULL,	'C' },
 68  		{ "force",			no_argument,	NULL,	'f' },
 69  		{ "glob",			no_argument,	NULL,	'g' },
 70  		{ "case-insensitive",		no_argument,	NULL,	'i' },
 71  		{ "dry-run",			no_argument,	NULL,	'n' },
 72  		{ "quiet",			no_argument,	NULL,	'q' },
 73  		{ "recursive",			no_argument,	NULL,	'R' },
 74  		{ "regex",			no_argument,	NULL,	'x' },
 75  		{ "yes",			no_argument,	NULL,	'y' },
 76  		{ NULL,				0,		NULL,	0   },
 77  	};
 78  
 79  	while ((ch = getopt_long(argc, argv, "aCfginqRxy", longopts, NULL)) != -1) {
 80  		switch (ch) {
 81  		case 'a':
 82  			match = MATCH_ALL;
 83  			break;
 84  		case 'C':
 85  			pkgdb_set_case_sensitivity(true);
 86  			break;
 87  		case 'f':
 88  			f |= PKG_FLAG_FORCE;
 89  			force = true;
 90  			break;
 91  		case 'g':
 92  			match = MATCH_GLOB;
 93  			break;
 94  		case 'i':
 95  			pkgdb_set_case_sensitivity(false);
 96  			break;
 97  		case 'n':
 98  			f |= PKG_FLAG_DRY_RUN;
 99  			lock_type = PKGDB_LOCK_READONLY;
100  			dry_run = true;
101  			break;
102  		case 'q':
103  			quiet = true;
104  			break;
105  		case 'R':
106  			recursive_flag = true;
107  			break;
108  		case 'x':
109  			match = MATCH_REGEX;
110  			break;
111  		case 'y':
112  			yes = true;
113  			break;
114  		default:
115  			usage_unregister();
116  			return (EXIT_FAILURE);
117  		}
118  	}
119  
120  	argc -= optind;
121  	argv += optind;
122  
123  	if (argc < 1 && match != MATCH_ALL) {
124  		usage_unregister();
125  		return (EXIT_FAILURE);
126  	}
127  
128  	if (dry_run)
129  		retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
130  	else
131  		retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
132  				       PKGDB_DB_LOCAL);
133  
134  	if (retcode == EPKG_ENODB) {
135  		warnx("No packages installed.  Nothing to do!");
136  		return (EXIT_SUCCESS);
137  	} else if (retcode == EPKG_ENOACCESS) {
138  		warnx("Insufficient privileges to delete packages");
139  		return (EXIT_FAILURE);
140  	} else if (retcode != EPKG_OK) {
141  		warnx("Error accessing the package database");
142  		return (EXIT_FAILURE);
143  	}
144  
145  	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
146  		return (EXIT_FAILURE);
147  
148  	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
149  		pkgdb_close(db);
150  		warnx("Cannot get an advisory lock on a database, it is locked by another process");
151  		return (EXIT_FAILURE);
152  	}
153  
154  
155  	if (pkg_jobs_new(&jobs, PKG_JOBS_DEINSTALL, db) != EPKG_OK) {
156  		pkgdb_close(db);
157  		return (EXIT_FAILURE);
158  	}
159  
160  	/*
161  	 * By default delete packages recursively.
162  	 * If force mode is enabled then we try to remove packages non-recursively.
163  	 * However, if -f and -R flags are both enabled then we return to
164  	 * recursive deletion.
165  	 */
166  	if (!force || recursive_flag)
167  		f |= PKG_FLAG_RECURSIVE;
168  
169  	pkg_jobs_set_flags(jobs, f);
170  
171  	if (match == MATCH_EXACT) {
172  		for (i = 0; i < argc; i++) {
173  			if (strchr(argv[i], '*') != NULL) {
174  				match = MATCH_GLOB;
175  				break;
176  			}
177  		}
178  	}
179  
180  	if (pkg_jobs_add(jobs, match, argv, argc) == EPKG_FATAL)
181  		goto cleanup;
182  
183  	if (pkg_jobs_solve(jobs) != EPKG_OK) {
184  		fprintf(stderr, "Cannot perform request\n");
185  		retcode = EXIT_FAILURE;
186  		goto cleanup;
187  	}
188  
189  	if (pkg_jobs_has_lockedpkgs(jobs)) {
190  		printf("The following package(s) are locked or vital and may not ");
191  		printf("be removed:\n\n");
192  		pkg_jobs_iter_lockedpkgs(jobs, print_pkg, &locked_pkgs);
193  		putchar('\n');
194  	}
195  
196  	/* check if we have something to deinstall */
197  	if ((nbactions = pkg_jobs_count(jobs)) == 0) {
198  		if (argc == 0) {
199  			if (!quiet)
200  				printf("Nothing to do.\n");
201  
202  			retcode = EXIT_SUCCESS;
203  			goto cleanup;
204  		}
205  		if (!quiet) {
206  			printf("%d packages requested for removal from the db: "
207  			    "%d locked, %d missing\n",
208  			    argc, locked_pkgs, argc - locked_pkgs);
209  		}
210  		if (locked_pkgs > 0) {
211  			retcode = EPKG_LOCKED;
212  		} else {
213  			retcode = EXIT_FAILURE;
214  		}
215  		goto cleanup;
216  	}
217  
218  	if (!quiet || dry_run) {
219  		if (!quiet) {
220  			print_jobs_summary(jobs,
221  				"Unregister has been requested for the following %d packages "
222  				"(of %d packages in the universe):\n\n", nbactions,
223  				pkg_jobs_total(jobs));
224  		}
225  		if (dry_run) {
226  			retcode = EXIT_SUCCESS;
227  			goto cleanup;
228  		}
229  		rc = query_yesno(false,
230  		            "\nProceed with unregistering packages? ");
231  	}
232  	else
233  		rc = yes;
234  
235  	if (!rc || (retcode = pkg_jobs_apply(jobs)) != EPKG_OK)
236  		goto cleanup;
237  
238  	if (messages != NULL) {
239  		fflush(messages->fp);
240  		printf("%s", messages->buf);
241  	}
242  	pkgdb_compact(db);
243  
244  	retcode = EXIT_SUCCESS;
245  
246  cleanup:
247  	pkgdb_release_lock(db, lock_type);
248  	pkg_jobs_free(jobs);
249  	pkgdb_close(db);
250  
251  	return (retcode);
252  }