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 }