Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2012 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 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer
12 : * in this position and unchanged.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 :
29 : #include <sys/param.h>
30 : #include <sys/queue.h>
31 : #include <sys/sbuf.h>
32 :
33 : #include <err.h>
34 : #include <assert.h>
35 : #include <getopt.h>
36 : #include <sysexits.h>
37 : #include <stdbool.h>
38 : #include <stdio.h>
39 : #include <string.h>
40 : #include <unistd.h>
41 :
42 : #include <pkg.h>
43 :
44 : #include "pkgcli.h"
45 :
46 : struct deps_entry {
47 : char *name;
48 : char *version;
49 : char *origin;
50 : STAILQ_ENTRY(deps_entry) next;
51 : };
52 :
53 : STAILQ_HEAD(deps_head, deps_entry);
54 :
55 : static int check_deps(struct pkgdb *db, struct pkg *pkg, struct deps_head *dh,
56 : bool noinstall, struct sbuf *out);
57 : static void add_missing_dep(struct pkg_dep *d, struct deps_head *dh, int *nbpkgs);
58 : static void deps_free(struct deps_head *dh);
59 : static int fix_deps(struct pkgdb *db, struct deps_head *dh, int nbpkgs);
60 : static void check_summary(struct pkgdb *db, struct deps_head *dh);
61 :
62 : static int
63 0 : check_deps(struct pkgdb *db, struct pkg *p, struct deps_head *dh, bool noinstall, struct sbuf *out)
64 : {
65 0 : struct pkg_dep *dep = NULL;
66 : struct pkgdb_it *it;
67 0 : char *buf = NULL;
68 0 : int nbpkgs = 0;
69 :
70 0 : assert(db != NULL);
71 0 : assert(p != NULL);
72 :
73 0 : while (pkg_deps(p, &dep) == EPKG_OK) {
74 : /* do we have a missing dependency? */
75 0 : if (pkg_is_installed(db, pkg_dep_name(dep)) != EPKG_OK) {
76 0 : if (quiet)
77 0 : pkg_sbuf_printf(out, "%n\t%sn\n", p, dep);
78 : else
79 0 : pkg_sbuf_printf(out, "%n has a missing dependency: %dn\n",
80 : p, dep);
81 0 : if (!noinstall)
82 0 : add_missing_dep(dep, dh, &nbpkgs);
83 : }
84 : }
85 :
86 : /* checking libraries required */
87 0 : buf = NULL;
88 0 : while (pkg_shlibs_required(p, &buf) == EPKG_OK) {
89 0 : it = pkgdb_query_shlib_provide(db, buf);
90 0 : if (it != NULL && pkgdb_it_count(it) > 0) {
91 0 : pkgdb_it_free(it);
92 0 : continue;
93 : }
94 0 : pkgdb_it_free(it);
95 0 : if (quiet)
96 0 : pkg_sbuf_printf(out, "%n\t%S\n", p, buf);
97 : else
98 0 : pkg_sbuf_printf(out, "%n has require a missing libraries: %S\n",
99 : p, buf);
100 : }
101 :
102 : /* checking requires */
103 0 : buf = NULL;
104 0 : while (pkg_requires(p, &buf) == EPKG_OK) {
105 0 : it = pkgdb_query_provide(db, buf);
106 0 : if (it != NULL && pkgdb_it_count(it) > 0) {
107 0 : pkgdb_it_free(it);
108 0 : continue;
109 : }
110 0 : pkgdb_it_free(it);
111 0 : if (quiet)
112 0 : pkg_sbuf_printf(out, "%n\tS\n", p, buf);
113 : else
114 0 : pkg_sbuf_printf(out, "%n has a missing requirement: %S\n",
115 : p, buf);
116 : }
117 :
118 0 : return (nbpkgs);
119 : }
120 :
121 : static void
122 0 : add_missing_dep(struct pkg_dep *d, struct deps_head *dh, int *nbpkgs)
123 : {
124 0 : struct deps_entry *e = NULL;
125 0 : const char *name = NULL;
126 :
127 0 : assert(d != NULL);
128 :
129 : /* do not add duplicate entries in the queue */
130 0 : name = pkg_dep_name(d);
131 :
132 0 : STAILQ_FOREACH(e, dh, next)
133 0 : if (strcmp(e->name, name) == 0)
134 0 : return;
135 :
136 0 : if ((e = calloc(1, sizeof(struct deps_entry))) == NULL)
137 0 : err(1, "calloc(deps_entry)");
138 :
139 0 : e->name = strdup(pkg_dep_name(d));
140 0 : e->version = strdup(pkg_dep_version(d));
141 0 : e->origin = strdup(pkg_dep_origin(d));
142 :
143 0 : (*nbpkgs)++;
144 :
145 0 : STAILQ_INSERT_TAIL(dh, e, next);
146 : }
147 :
148 : static void
149 0 : deps_free(struct deps_head *dh)
150 : {
151 0 : struct deps_entry *e = NULL;
152 :
153 0 : while (!STAILQ_EMPTY(dh)) {
154 0 : e = STAILQ_FIRST(dh);
155 0 : STAILQ_REMOVE_HEAD(dh, next);
156 0 : free(e->name);
157 0 : free(e->version);
158 0 : free(e->origin);
159 0 : free(e);
160 : }
161 0 : }
162 :
163 : static int
164 0 : fix_deps(struct pkgdb *db, struct deps_head *dh, int nbpkgs)
165 : {
166 0 : struct pkg_jobs *jobs = NULL;
167 0 : struct deps_entry *e = NULL;
168 0 : char **pkgs = NULL;
169 0 : int i = 0;
170 : bool rc;
171 0 : pkg_flags f = PKG_FLAG_AUTOMATIC;
172 :
173 0 : assert(db != NULL);
174 0 : assert(nbpkgs > 0);
175 :
176 0 : if ((pkgs = calloc(nbpkgs, sizeof (char *))) == NULL)
177 0 : err(1, "calloc()");
178 :
179 0 : STAILQ_FOREACH(e, dh, next)
180 0 : pkgs[i++] = e->name;
181 :
182 0 : if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) {
183 0 : free(pkgs);
184 0 : return (EPKG_ENODB);
185 : }
186 :
187 0 : if (pkg_jobs_new(&jobs, PKG_JOBS_INSTALL, db) != EPKG_OK) {
188 0 : goto cleanup;
189 : }
190 :
191 0 : pkg_jobs_set_flags(jobs, f);
192 :
193 0 : if (pkg_jobs_add(jobs, MATCH_EXACT, pkgs, nbpkgs) == EPKG_FATAL) {
194 0 : goto cleanup;
195 : }
196 :
197 0 : if (pkg_jobs_solve(jobs) != EPKG_OK) {
198 0 : goto cleanup;
199 : }
200 :
201 0 : if (pkg_jobs_count(jobs) == 0) {
202 0 : printf("\nUnable to find packages for installation.\n\n");
203 0 : goto cleanup;
204 : }
205 :
206 : /* print a summary before applying the jobs */
207 0 : print_jobs_summary(jobs, "The following packages will be installed:\n\n");
208 :
209 0 : rc = query_yesno(false, "\n>>> Try to fix the missing dependencies? ");
210 :
211 0 : if (rc) {
212 0 : if (pkgdb_access(PKGDB_MODE_WRITE, PKGDB_DB_LOCAL) ==
213 : EPKG_ENOACCESS) {
214 0 : warnx("Insufficient privileges to modify the package "
215 : "database");
216 :
217 0 : goto cleanup;
218 : }
219 :
220 0 : pkg_jobs_apply(jobs);
221 : }
222 :
223 : cleanup:
224 0 : free(pkgs);
225 0 : if (jobs != NULL)
226 0 : pkg_jobs_free(jobs);
227 :
228 0 : return (EPKG_OK);
229 : }
230 :
231 : static void
232 0 : check_summary(struct pkgdb *db, struct deps_head *dh)
233 : {
234 0 : struct deps_entry *e = NULL;
235 0 : struct pkg *pkg = NULL;
236 0 : struct pkgdb_it *it = NULL;
237 0 : bool fixed = true;
238 :
239 0 : assert(db != NULL);
240 :
241 0 : printf(">>> Summary of actions performed:\n\n");
242 :
243 0 : STAILQ_FOREACH(e, dh, next) {
244 0 : if ((it = pkgdb_query(db, e->name, MATCH_EXACT)) == NULL)
245 0 : return;
246 :
247 0 : if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
248 0 : fixed = false;
249 0 : printf("%s dependency failed to be fixed\n", e->name);
250 : } else
251 0 : printf("%s dependency has been fixed\n", e->name);
252 :
253 0 : pkgdb_it_free(it);
254 : }
255 :
256 0 : if (fixed) {
257 0 : printf("\n>>> Missing dependencies were fixed successfully.\n");
258 : } else {
259 0 : printf("\n>>> There are still missing dependencies.\n");
260 0 : printf(">>> You are advised to try fixing them manually.\n");
261 0 : printf("\n>>> Also make sure to check 'pkg updating' for known issues.\n");
262 : }
263 :
264 0 : pkg_free(pkg);
265 : }
266 :
267 : void
268 0 : usage_check(void)
269 : {
270 0 : fprintf(stderr, "Usage: pkg check [-Bdsr] [-qvy] [-a | -Cgix <pattern>]\n\n");
271 0 : fprintf(stderr, "For more information see 'pkg help check'.\n");
272 0 : }
273 :
274 : int
275 0 : exec_check(int argc, char **argv)
276 : {
277 0 : struct pkg *pkg = NULL;
278 0 : struct pkgdb_it *it = NULL;
279 0 : struct pkgdb *db = NULL;
280 0 : struct sbuf *msg = NULL;
281 0 : match_t match = MATCH_EXACT;
282 0 : int flags = PKG_LOAD_BASIC;
283 0 : int ret, rc = EX_OK;
284 : int ch;
285 0 : bool dcheck = false;
286 0 : bool checksums = false;
287 0 : bool recompute = false;
288 0 : bool reanalyse_shlibs = false;
289 0 : bool noinstall = false;
290 0 : int nbpkgs = 0;
291 0 : int i, processed, total = 0;
292 0 : int verbose = 0;
293 :
294 0 : struct option longopts[] = {
295 : { "all", no_argument, NULL, 'a' },
296 : { "shlibs", no_argument, NULL, 'B' },
297 : { "case-sensitive", no_argument, NULL, 'C' },
298 : { "dependencies", no_argument, NULL, 'd' },
299 : { "glob", no_argument, NULL, 'g' },
300 : { "case-insensitive", no_argument, NULL, 'i' },
301 : { "dry-run", no_argument, NULL, 'n' },
302 : { "recompute", no_argument, NULL, 'r' },
303 : { "checksums", no_argument, NULL, 's' },
304 : { "verbose", no_argument, NULL, 'v' },
305 : { "quiet", no_argument, NULL, 'q' },
306 : { "regex", no_argument, NULL, 'x' },
307 : { "yes", no_argument, NULL, 'y' },
308 : { NULL, 0, NULL, 0 },
309 : };
310 :
311 0 : struct deps_head dh = STAILQ_HEAD_INITIALIZER(dh);
312 :
313 0 : processed = 0;
314 :
315 0 : while ((ch = getopt_long(argc, argv, "+aBCdginqrsvxy", longopts, NULL)) != -1) {
316 0 : switch (ch) {
317 : case 'a':
318 0 : match = MATCH_ALL;
319 0 : break;
320 : case 'B':
321 0 : reanalyse_shlibs = true;
322 0 : flags |= PKG_LOAD_FILES;
323 0 : break;
324 : case 'C':
325 0 : pkgdb_set_case_sensitivity(true);
326 0 : break;
327 : case 'd':
328 0 : dcheck = true;
329 0 : flags |= PKG_LOAD_DEPS|PKG_LOAD_REQUIRES|PKG_LOAD_SHLIBS_REQUIRED;
330 0 : break;
331 : case 'g':
332 0 : match = MATCH_GLOB;
333 0 : break;
334 : case 'i':
335 0 : pkgdb_set_case_sensitivity(false);
336 0 : break;
337 : case 'n':
338 0 : noinstall = true;
339 0 : break;
340 : case 'q':
341 0 : quiet = true;
342 0 : break;
343 : case 'r':
344 0 : recompute = true;
345 0 : flags |= PKG_LOAD_FILES;
346 0 : break;
347 : case 's':
348 0 : checksums = true;
349 0 : flags |= PKG_LOAD_FILES;
350 0 : break;
351 : case 'v':
352 0 : verbose = 1;
353 0 : break;
354 : case 'x':
355 0 : match = MATCH_REGEX;
356 0 : break;
357 : case 'y':
358 0 : yes = true;
359 0 : break;
360 : default:
361 0 : usage_check();
362 0 : return (EX_USAGE);
363 : }
364 : }
365 0 : argc -= optind;
366 0 : argv += optind;
367 :
368 : /* Default to all packages if no pkg provided */
369 0 : if (argc == 0 && (dcheck || checksums || recompute || reanalyse_shlibs)) {
370 0 : match = MATCH_ALL;
371 0 : } else if ((argc == 0 && match != MATCH_ALL) || !(dcheck || checksums || recompute || reanalyse_shlibs)) {
372 0 : usage_check();
373 0 : return (EX_USAGE);
374 : }
375 :
376 0 : if (recompute || reanalyse_shlibs)
377 0 : ret = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
378 : PKGDB_DB_LOCAL);
379 : else
380 0 : ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
381 :
382 0 : if (ret == EPKG_ENODB) {
383 0 : if (!quiet)
384 0 : warnx("No packages installed. Nothing to do!");
385 0 : return (EX_OK);
386 0 : } else if (ret == EPKG_ENOACCESS) {
387 0 : warnx("Insufficient privileges to access the package database");
388 0 : return (EX_NOPERM);
389 0 : } else if (ret != EPKG_OK) {
390 0 : warnx("Error accessing the package database");
391 0 : return (EX_SOFTWARE);
392 : }
393 :
394 0 : if (pkgdb_access(PKGDB_MODE_WRITE, PKGDB_DB_LOCAL) == EPKG_ENOACCESS) {
395 0 : warnx("Insufficient privileges");
396 0 : return (EX_NOPERM);
397 : }
398 :
399 0 : ret = pkgdb_open(&db, PKGDB_DEFAULT);
400 0 : if (ret != EPKG_OK)
401 0 : return (EX_IOERR);
402 :
403 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY) != EPKG_OK) {
404 0 : pkgdb_close(db);
405 0 : warnx("Cannot get an advisory lock on a database, it is locked by another process");
406 0 : return (EX_TEMPFAIL);
407 : }
408 :
409 0 : i = 0;
410 0 : nbdone = 0;
411 : do {
412 : /* XXX: This is really quirky, it would be cleaner to pass
413 : * in multiple matches and only run this top-loop once. */
414 0 : if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
415 0 : rc = EX_IOERR;
416 0 : goto cleanup;
417 : }
418 :
419 0 : if (msg == NULL)
420 0 : msg = sbuf_new_auto();
421 0 : if (!verbose) {
422 0 : if (!quiet) {
423 0 : if (match == MATCH_ALL)
424 0 : progressbar_start("Checking all packages");
425 : else {
426 0 : sbuf_printf(msg, "Checking %s", argv[i]);
427 0 : sbuf_finish(msg);
428 0 : progressbar_start(sbuf_data(msg));
429 : }
430 : }
431 0 : processed = 0;
432 0 : total = pkgdb_it_count(it);
433 : } else {
434 0 : if (match == MATCH_ALL)
435 0 : nbactions = pkgdb_it_count(it);
436 : else
437 0 : nbactions = argc;
438 : }
439 :
440 0 : struct sbuf *out = sbuf_new_auto();
441 0 : while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
442 0 : if (!quiet) {
443 0 : if (!verbose)
444 0 : progressbar_tick(processed, total);
445 : else {
446 0 : ++nbdone;
447 0 : job_status_begin(msg);
448 0 : pkg_sbuf_printf(msg, "Checking %n-%v:",
449 : pkg, pkg);
450 0 : sbuf_flush(msg);
451 : }
452 : }
453 :
454 : /* check for missing dependencies */
455 0 : if (dcheck) {
456 0 : if (!quiet && verbose)
457 0 : printf(" dependencies...");
458 0 : nbpkgs += check_deps(db, pkg, &dh, noinstall, out);
459 0 : if (noinstall && nbpkgs > 0) {
460 0 : rc = EX_UNAVAILABLE;
461 : }
462 : }
463 0 : if (checksums) {
464 0 : if (!quiet && verbose)
465 0 : printf(" checksums...");
466 0 : if (pkg_test_filesum(pkg) != EPKG_OK) {
467 0 : rc = EX_DATAERR;
468 : }
469 : }
470 0 : if (recompute) {
471 0 : if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
472 : PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
473 0 : if (!quiet && verbose)
474 0 : printf(" recomputing...");
475 0 : if (pkg_recompute(db, pkg) != EPKG_OK) {
476 0 : rc = EX_DATAERR;
477 : }
478 0 : pkgdb_downgrade_lock(db,
479 : PKGDB_LOCK_EXCLUSIVE,
480 : PKGDB_LOCK_ADVISORY);
481 : }
482 : else {
483 0 : rc = EX_TEMPFAIL;
484 : }
485 : }
486 0 : if (reanalyse_shlibs) {
487 0 : if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
488 : PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
489 0 : if (!quiet && verbose)
490 0 : printf(" shared libraries...");
491 0 : if (pkgdb_reanalyse_shlibs(db, pkg) != EPKG_OK) {
492 0 : pkg_fprintf(stderr, "Failed to "
493 : "reanalyse for shlibs: "
494 : "%n-%v\n", pkg, pkg);
495 0 : rc = EX_UNAVAILABLE;
496 : }
497 0 : pkgdb_downgrade_lock(db,
498 : PKGDB_LOCK_EXCLUSIVE,
499 : PKGDB_LOCK_ADVISORY);
500 : }
501 : else {
502 0 : rc = EX_TEMPFAIL;
503 : }
504 : }
505 :
506 0 : if (!quiet) {
507 0 : if (!verbose)
508 0 : ++processed;
509 : else
510 0 : printf(" done\n");
511 : }
512 : }
513 0 : if (!quiet && !verbose)
514 0 : progressbar_tick(processed, total);
515 0 : if (sbuf_len(out) > 0) {
516 0 : sbuf_finish(out);
517 0 : printf("%s", sbuf_data(out));
518 : }
519 0 : sbuf_delete(out);
520 0 : if (msg != NULL) {
521 0 : sbuf_delete(msg);
522 0 : msg = NULL;
523 : }
524 :
525 0 : if (dcheck && nbpkgs > 0 && !noinstall) {
526 0 : printf("\n>>> Missing package dependencies were detected.\n");
527 0 : printf(">>> Found %d issue(s) in the package database.\n\n", nbpkgs);
528 0 : if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
529 : PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
530 0 : ret = fix_deps(db, &dh, nbpkgs);
531 0 : if (ret == EPKG_OK)
532 0 : check_summary(db, &dh);
533 0 : else if (ret == EPKG_ENODB) {
534 0 : db = NULL;
535 0 : rc = EX_IOERR;
536 : }
537 0 : if (rc == EX_IOERR)
538 0 : goto cleanup;
539 0 : pkgdb_downgrade_lock(db, PKGDB_LOCK_EXCLUSIVE,
540 : PKGDB_LOCK_ADVISORY);
541 : }
542 : else {
543 0 : rc = EX_TEMPFAIL;
544 0 : goto cleanup;
545 : }
546 : }
547 0 : pkgdb_it_free(it);
548 0 : i++;
549 0 : } while (i < argc);
550 :
551 : cleanup:
552 0 : if (!verbose)
553 0 : progressbar_stop();
554 0 : if (msg != NULL)
555 0 : sbuf_delete(msg);
556 0 : deps_free(&dh);
557 0 : pkg_free(pkg);
558 0 : pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
559 0 : pkgdb_close(db);
560 :
561 0 : return (rc);
562 : }
|