check.c
1 /*- 2 * Copyright (c) 2011-2025 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com> 4 * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org> 5 * Copyright (c) 2016 Vsevolod Stakhov <vsevolod@FreeBSD.org> 6 * 7 * SPDX-License-Identifier: BSD-2-Clause 8 */ 9 10 #include <sys/param.h> 11 12 #include <err.h> 13 #include <assert.h> 14 #include <getopt.h> 15 #include <stdbool.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include <pkg.h> 22 #include <xmalloc.h> 23 24 #include "pkgcli.h" 25 26 static int check_deps(struct pkgdb *db, struct pkg *pkg, charv_t *dh, 27 bool noinstall, xstring *out); 28 static void add_missing_dep(struct pkg_dep *d, charv_t *dh, int *nbpkgs); 29 static int fix_deps(struct pkgdb *db, charv_t *dh, int nbpkgs); 30 static void check_summary(struct pkgdb *db, charv_t *dh); 31 32 static int 33 check_deps(struct pkgdb *db, struct pkg *p, charv_t *dh, bool noinstall, xstring *out) 34 { 35 struct pkg_dep *dep = NULL; 36 struct pkgdb_it *it; 37 const char *buf; 38 int nbpkgs = 0; 39 struct pkg_stringlist *sl = NULL; 40 struct pkg_stringlist_iterator *slit; 41 struct pkgbase *pb; 42 43 assert(db != NULL); 44 assert(p != NULL); 45 46 while (pkg_deps(p, &dep) == EPKG_OK) { 47 /* do we have a missing dependency? */ 48 if (pkg_is_installed(db, pkg_dep_name(dep)) != EPKG_OK) { 49 if (quiet) 50 pkg_fprintf(out->fp, "%n\t%dn\n", p, dep); 51 else 52 pkg_fprintf(out->fp, "%n has a missing dependency: %dn\n", 53 p, dep); 54 if (!noinstall) 55 add_missing_dep(dep, dh, &nbpkgs); 56 } 57 } 58 59 /* checking libraries required */ 60 pkg_get(p, PKG_ATTR_SHLIBS_REQUIRED, &sl); 61 pb = pkgbase_new(db); 62 slit = pkg_stringlist_iterator(sl); 63 while ((buf = pkg_stringlist_next(slit))) { 64 if (pkgbase_provide_shlib(pb, buf)) 65 continue; 66 it = pkgdb_query_shlib_provide(db, buf); 67 if (it != NULL && pkgdb_it_count(it) > 0) { 68 pkgdb_it_free(it); 69 continue; 70 } 71 pkgdb_it_free(it); 72 if (quiet) 73 pkg_fprintf(out->fp, "%n\t%S\n", p, buf); 74 else 75 pkg_fprintf(out->fp, "%n is missing a required shared library: %S\n", 76 p, buf); 77 } 78 free(slit); 79 free(sl); 80 81 /* checking requires */ 82 buf = NULL; 83 pkg_get(p, PKG_ATTR_REQUIRES, &sl); 84 slit = pkg_stringlist_iterator(sl); 85 while ((buf = pkg_stringlist_next(slit))) { 86 if (pkgbase_provide(pb, buf)) 87 continue; 88 it = pkgdb_query_provide(db, buf); 89 if (it != NULL && pkgdb_it_count(it) > 0) { 90 pkgdb_it_free(it); 91 continue; 92 } 93 pkgdb_it_free(it); 94 if (quiet) 95 pkg_fprintf(out->fp, "%n\t%S\n", p, buf); 96 else 97 pkg_fprintf(out->fp, "%n has a missing requirement: %S\n", 98 p, buf); 99 } 100 pkgbase_free(pb); 101 free(slit); 102 free(sl); 103 104 return (nbpkgs); 105 } 106 107 static void 108 add_missing_dep(struct pkg_dep *d, charv_t *dh, int *nbpkgs) 109 { 110 const char *name = NULL; 111 112 assert(d != NULL); 113 114 /* do not add duplicate entries in the queue */ 115 name = pkg_dep_name(d); 116 117 vec_foreach(*dh, i) { 118 if (STREQ(dh->d[i], name)) 119 return; 120 } 121 (*nbpkgs)++; 122 123 vec_push(dh, xstrdup(name)); 124 } 125 126 static int 127 fix_deps(struct pkgdb *db, charv_t *dh, int nbpkgs) 128 { 129 struct pkg_jobs *jobs = NULL; 130 bool rc; 131 pkg_flags f = PKG_FLAG_AUTOMATIC; 132 133 assert(db != NULL); 134 assert(nbpkgs > 0); 135 136 if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) { 137 return (EPKG_ENODB); 138 } 139 140 if (pkg_jobs_new(&jobs, PKG_JOBS_INSTALL, db) != EPKG_OK) { 141 goto cleanup; 142 } 143 144 pkg_jobs_set_flags(jobs, f); 145 146 if (pkg_jobs_add(jobs, MATCH_EXACT, dh->d, dh->len) == EPKG_FATAL) { 147 goto cleanup; 148 } 149 150 if (pkg_jobs_solve(jobs) != EPKG_OK) { 151 goto cleanup; 152 } 153 154 if (pkg_jobs_count(jobs) == 0) { 155 printf("\nUnable to find packages for installation.\n\n"); 156 goto cleanup; 157 } 158 159 /* print a summary before applying the jobs */ 160 print_jobs_summary(jobs, 161 "The following packages will be installed:\n\n"); 162 163 rc = query_yesno(false, "\n>>> Try to fix the missing dependencies? "); 164 165 if (rc) { 166 if (pkgdb_access(PKGDB_MODE_WRITE, PKGDB_DB_LOCAL) == 167 EPKG_ENOACCESS) { 168 warnx("Insufficient privileges to modify the package " 169 "database"); 170 171 goto cleanup; 172 } 173 174 pkg_jobs_apply(jobs); 175 } 176 177 cleanup: 178 if (jobs != NULL) 179 pkg_jobs_free(jobs); 180 181 return (EPKG_OK); 182 } 183 184 static void 185 check_summary(struct pkgdb *db, charv_t *dh) 186 { 187 struct pkg *pkg = NULL; 188 struct pkgdb_it *it = NULL; 189 bool fixed = true; 190 191 assert(db != NULL); 192 193 printf(">>> Summary of actions performed:\n\n"); 194 195 vec_foreach(*dh, i) { 196 if ((it = pkgdb_query(db, dh->d[i], MATCH_EXACT)) == NULL) 197 return; 198 199 if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) { 200 fixed = false; 201 printf("%s dependency failed to be fixed\n", dh->d[i]); 202 } else 203 printf("%s dependency has been fixed\n", dh->d[i]); 204 205 pkgdb_it_free(it); 206 } 207 208 if (fixed) { 209 printf("\n>>> Missing dependencies were fixed successfully.\n"); 210 } else { 211 printf("\n>>> There are still missing dependencies.\n"); 212 printf(">>> Try fixing them manually.\n"); 213 printf("\n>>> Also make sure to check 'pkg updating' for known issues.\n"); 214 } 215 216 pkg_free(pkg); 217 } 218 219 void 220 usage_check(void) 221 { 222 fprintf(stderr, 223 "Usage: pkg check -d[n]|-s [-qvy] -a\n"); 224 fprintf(stderr, 225 " pkg check -d[n]|-s [-qvy] [-Cgix] <pattern>\n\n"); 226 fprintf(stderr, "For more information see 'pkg help check'.\n"); 227 } 228 229 int 230 exec_check(int argc, char **argv) 231 { 232 struct pkg *pkg = NULL; 233 struct pkgdb_it *it = NULL; 234 struct pkgdb *db = NULL; 235 xstring *msg = NULL; 236 match_t match = MATCH_EXACT; 237 int flags = PKG_LOAD_BASIC; 238 int ret, rc = EXIT_SUCCESS; 239 int ch; 240 bool dcheck = false; 241 bool checksums = false; 242 bool metadata = false; 243 bool noinstall = false; 244 int nbpkgs = 0; 245 int i, processed, total = 0; 246 int verbose = 0; 247 int nbactions; 248 charv_t dh = vec_init(); 249 250 struct option longopts[] = { 251 { "all", no_argument, NULL, 'a' }, 252 { "shlibs", no_argument, NULL, 'B' }, 253 { "case-sensitive", no_argument, NULL, 'C' }, 254 { "dependencies", no_argument, NULL, 'd' }, 255 { "glob", no_argument, NULL, 'g' }, 256 { "case-insensitive", no_argument, NULL, 'i' }, 257 { "metadata", no_argument, NULL, 'm' }, 258 { "dry-run", no_argument, NULL, 'n' }, 259 { "recompute", no_argument, NULL, 'r' }, 260 { "checksums", no_argument, NULL, 's' }, 261 { "verbose", no_argument, NULL, 'v' }, 262 { "quiet", no_argument, NULL, 'q' }, 263 { "regex", no_argument, NULL, 'x' }, 264 { "yes", no_argument, NULL, 'y' }, 265 { NULL, 0, NULL, 0 }, 266 }; 267 268 processed = 0; 269 270 while ((ch = getopt_long(argc, argv, "+aBCdgimnqrsvxy", longopts, NULL)) != -1) { 271 switch (ch) { 272 case 'a': 273 match = MATCH_ALL; 274 break; 275 case 'B': 276 /* backward compatibility but do nothing */ 277 break; 278 case 'C': 279 pkgdb_set_case_sensitivity(true); 280 break; 281 case 'd': 282 dcheck = true; 283 flags |= PKG_LOAD_DEPS|PKG_LOAD_REQUIRES|PKG_LOAD_SHLIBS_REQUIRED; 284 break; 285 case 'g': 286 match = MATCH_GLOB; 287 break; 288 case 'i': 289 pkgdb_set_case_sensitivity(false); 290 break; 291 case 'm': 292 metadata = true; 293 flags |= PKG_LOAD_FILES|PKG_LOAD_DIRS; 294 break; 295 case 'n': 296 noinstall = true; 297 break; 298 case 'q': 299 quiet = true; 300 break; 301 case 'r': 302 /* backward compatibility but do nothing */ 303 break; 304 case 's': 305 checksums = true; 306 flags |= PKG_LOAD_FILES; 307 break; 308 case 'v': 309 verbose = 1; 310 break; 311 case 'x': 312 match = MATCH_REGEX; 313 break; 314 case 'y': 315 yes = true; 316 break; 317 default: 318 usage_check(); 319 return (EXIT_FAILURE); 320 } 321 } 322 argc -= optind; 323 argv += optind; 324 325 if (!(dcheck || checksums || metadata)) { 326 checksums = true; 327 flags |= PKG_LOAD_FILES; 328 } 329 /* Default to all packages if no pkg provided */ 330 if (argc == 0 && (dcheck || checksums || metadata)) { 331 match = MATCH_ALL; 332 } else if ((argc == 0 && match != MATCH_ALL) || !(dcheck || checksums || metadata)) { 333 usage_check(); 334 return (EXIT_FAILURE); 335 } 336 337 ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL); 338 339 if (ret == EPKG_ENODB) { 340 if (!quiet) 341 warnx("No packages installed. Nothing to do!"); 342 return (EXIT_SUCCESS); 343 } else if (ret == EPKG_ENOACCESS) { 344 warnx("Insufficient privileges to access the package database"); 345 return (EXIT_FAILURE); 346 } else if (ret != EPKG_OK) { 347 warnx("Error accessing the package database"); 348 return (EXIT_FAILURE); 349 } 350 351 ret = pkgdb_open(&db, PKGDB_DEFAULT); 352 if (ret != EPKG_OK) 353 return (EXIT_FAILURE); 354 355 i = 0; 356 do { 357 /* XXX: This is really quirky, it would be cleaner to pass 358 * in multiple matches and only run this top-loop once. */ 359 if ((it = pkgdb_query(db, argv[i], match)) == NULL) { 360 rc = EXIT_FAILURE; 361 break; 362 } 363 nbactions = pkgdb_it_count(it); 364 if (nbactions == 0 && match != MATCH_ALL) { 365 warnx("No packages matching: %s", argv[i]); 366 rc = EXIT_FAILURE; 367 pkgdb_it_free(it); 368 it = NULL; 369 break; 370 } 371 372 if (msg == NULL) 373 msg = xstring_new(); 374 if (!verbose) { 375 if (!quiet) { 376 if (match == MATCH_ALL) 377 progressbar_start("Checking all packages"); 378 else { 379 fprintf(msg->fp, "Checking %s", argv[i]); 380 fflush(msg->fp); 381 progressbar_start(msg->buf); 382 } 383 } 384 processed = 0; 385 total = pkgdb_it_count(it); 386 } 387 388 xstring *out = xstring_new(); 389 while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) { 390 if (!quiet) { 391 if (!verbose) 392 progressbar_tick(processed, total); 393 else { 394 job_status_begin(msg); 395 pkg_fprintf(msg->fp, "Checking %n-%v:", 396 pkg, pkg); 397 fflush(msg->fp); 398 printf("%s", msg->buf); 399 xstring_reset(msg); 400 } 401 } 402 403 /* check for missing dependencies */ 404 if (dcheck) { 405 if (!quiet && verbose) 406 printf(" dependencies..."); 407 nbpkgs += check_deps(db, pkg, &dh, noinstall, out); 408 if (noinstall && nbpkgs > 0) { 409 rc = EXIT_FAILURE; 410 } 411 } 412 if (checksums || metadata) { 413 if (!quiet && verbose) 414 printf("%s%s", checksums ? " checksums..." : "", 415 metadata ? " metadata...": ""); 416 if (pkg_check_files(pkg, checksums, metadata) != EPKG_OK) { 417 rc = EXIT_FAILURE; 418 } 419 } 420 421 if (!quiet) { 422 if (!verbose) 423 ++processed; 424 else 425 printf(" done\n"); 426 } 427 } 428 pkgdb_it_free(it); 429 it = NULL; 430 431 if (!quiet && !verbose) 432 progressbar_tick(processed, total); 433 fflush(out->fp); 434 if (out->buf[0] != '\0') { 435 printf("%s", out->buf); 436 } 437 xstring_free(out); 438 xstring_free(msg); 439 msg = NULL; 440 441 if (dcheck && nbpkgs > 0 && !noinstall) { 442 printf("\n>>> Missing package dependencies were detected.\n"); 443 printf(">>> Found %d issue(s) in the package database.\n\n", nbpkgs); 444 if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY, 445 PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) { 446 ret = fix_deps(db, &dh, nbpkgs); 447 if (ret == EPKG_OK) 448 check_summary(db, &dh); 449 else if (ret == EPKG_ENODB) { 450 db = NULL; 451 rc = EXIT_FAILURE; 452 } 453 if (rc == EXIT_FAILURE) 454 break; 455 pkgdb_downgrade_lock(db, PKGDB_LOCK_EXCLUSIVE, 456 PKGDB_LOCK_ADVISORY); 457 } 458 else { 459 rc = EXIT_FAILURE; 460 break; 461 } 462 } 463 i++; 464 } while (i < argc); 465 assert(it == NULL); 466 467 if (!verbose) 468 progressbar_stop(); 469 xstring_free(msg); 470 vec_free_and_free(&dh, free); 471 pkg_free(pkg); 472 pkgdb_close(db); 473 474 return (rc); 475 }