/ src / check.c
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  }