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