Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/check.c
2065 views
1
/*-
2
* Copyright (c) 2011-2025 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Marin Atanasov Nikolov <[email protected]>
4
* Copyright (c) 2014 Matthew Seaman <[email protected]>
5
* Copyright (c) 2016 Vsevolod Stakhov <[email protected]>
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 noinstall = false;
243
int nbpkgs = 0;
244
int i, processed, total = 0;
245
int verbose = 0;
246
int nbactions;
247
charv_t dh = vec_init();
248
249
struct option longopts[] = {
250
{ "all", no_argument, NULL, 'a' },
251
{ "shlibs", no_argument, NULL, 'B' },
252
{ "case-sensitive", no_argument, NULL, 'C' },
253
{ "dependencies", no_argument, NULL, 'd' },
254
{ "glob", no_argument, NULL, 'g' },
255
{ "case-insensitive", no_argument, NULL, 'i' },
256
{ "dry-run", no_argument, NULL, 'n' },
257
{ "recompute", no_argument, NULL, 'r' },
258
{ "checksums", no_argument, NULL, 's' },
259
{ "verbose", no_argument, NULL, 'v' },
260
{ "quiet", no_argument, NULL, 'q' },
261
{ "regex", no_argument, NULL, 'x' },
262
{ "yes", no_argument, NULL, 'y' },
263
{ NULL, 0, NULL, 0 },
264
};
265
266
processed = 0;
267
268
while ((ch = getopt_long(argc, argv, "+aBCdginqrsvxy", longopts, NULL)) != -1) {
269
switch (ch) {
270
case 'a':
271
match = MATCH_ALL;
272
break;
273
case 'B':
274
/* backward compatibility but do nothing */
275
break;
276
case 'C':
277
pkgdb_set_case_sensitivity(true);
278
break;
279
case 'd':
280
dcheck = true;
281
flags |= PKG_LOAD_DEPS|PKG_LOAD_REQUIRES|PKG_LOAD_SHLIBS_REQUIRED;
282
break;
283
case 'g':
284
match = MATCH_GLOB;
285
break;
286
case 'i':
287
pkgdb_set_case_sensitivity(false);
288
break;
289
case 'n':
290
noinstall = true;
291
break;
292
case 'q':
293
quiet = true;
294
break;
295
case 'r':
296
/* backward compatibility but do nothing */
297
break;
298
case 's':
299
checksums = true;
300
flags |= PKG_LOAD_FILES;
301
break;
302
case 'v':
303
verbose = 1;
304
break;
305
case 'x':
306
match = MATCH_REGEX;
307
break;
308
case 'y':
309
yes = true;
310
break;
311
default:
312
usage_check();
313
return (EXIT_FAILURE);
314
}
315
}
316
argc -= optind;
317
argv += optind;
318
319
if (!(dcheck || checksums)) {
320
checksums = true;
321
flags |= PKG_LOAD_FILES;
322
}
323
/* Default to all packages if no pkg provided */
324
if (argc == 0 && (dcheck || checksums)) {
325
match = MATCH_ALL;
326
} else if ((argc == 0 && match != MATCH_ALL) || !(dcheck || checksums)) {
327
usage_check();
328
return (EXIT_FAILURE);
329
}
330
331
ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
332
333
if (ret == EPKG_ENODB) {
334
if (!quiet)
335
warnx("No packages installed. Nothing to do!");
336
return (EXIT_SUCCESS);
337
} else if (ret == EPKG_ENOACCESS) {
338
warnx("Insufficient privileges to access the package database");
339
return (EXIT_FAILURE);
340
} else if (ret != EPKG_OK) {
341
warnx("Error accessing the package database");
342
return (EXIT_FAILURE);
343
}
344
345
ret = pkgdb_open(&db, PKGDB_DEFAULT);
346
if (ret != EPKG_OK)
347
return (EXIT_FAILURE);
348
349
i = 0;
350
do {
351
/* XXX: This is really quirky, it would be cleaner to pass
352
* in multiple matches and only run this top-loop once. */
353
if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
354
rc = EXIT_FAILURE;
355
break;
356
}
357
nbactions = pkgdb_it_count(it);
358
if (nbactions == 0 && match != MATCH_ALL) {
359
warnx("No packages matching: %s", argv[i]);
360
rc = EXIT_FAILURE;
361
pkgdb_it_free(it);
362
it = NULL;
363
break;
364
}
365
366
if (msg == NULL)
367
msg = xstring_new();
368
if (!verbose) {
369
if (!quiet) {
370
if (match == MATCH_ALL)
371
progressbar_start("Checking all packages");
372
else {
373
fprintf(msg->fp, "Checking %s", argv[i]);
374
fflush(msg->fp);
375
progressbar_start(msg->buf);
376
}
377
}
378
processed = 0;
379
total = pkgdb_it_count(it);
380
}
381
382
xstring *out = xstring_new();
383
while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
384
if (!quiet) {
385
if (!verbose)
386
progressbar_tick(processed, total);
387
else {
388
job_status_begin(msg);
389
pkg_fprintf(msg->fp, "Checking %n-%v:",
390
pkg, pkg);
391
fflush(msg->fp);
392
printf("%s", msg->buf);
393
xstring_reset(msg);
394
}
395
}
396
397
/* check for missing dependencies */
398
if (dcheck) {
399
if (!quiet && verbose)
400
printf(" dependencies...");
401
nbpkgs += check_deps(db, pkg, &dh, noinstall, out);
402
if (noinstall && nbpkgs > 0) {
403
rc = EXIT_FAILURE;
404
}
405
}
406
if (checksums) {
407
if (!quiet && verbose)
408
printf(" checksums...");
409
if (pkg_test_filesum(pkg) != EPKG_OK) {
410
rc = EXIT_FAILURE;
411
}
412
}
413
414
if (!quiet) {
415
if (!verbose)
416
++processed;
417
else
418
printf(" done\n");
419
}
420
}
421
pkgdb_it_free(it);
422
it = NULL;
423
424
if (!quiet && !verbose)
425
progressbar_tick(processed, total);
426
fflush(out->fp);
427
if (out->buf[0] != '\0') {
428
printf("%s", out->buf);
429
}
430
xstring_free(out);
431
xstring_free(msg);
432
msg = NULL;
433
434
if (dcheck && nbpkgs > 0 && !noinstall) {
435
printf("\n>>> Missing package dependencies were detected.\n");
436
printf(">>> Found %d issue(s) in the package database.\n\n", nbpkgs);
437
if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
438
PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
439
ret = fix_deps(db, &dh, nbpkgs);
440
if (ret == EPKG_OK)
441
check_summary(db, &dh);
442
else if (ret == EPKG_ENODB) {
443
db = NULL;
444
rc = EXIT_FAILURE;
445
}
446
if (rc == EXIT_FAILURE)
447
break;
448
pkgdb_downgrade_lock(db, PKGDB_LOCK_EXCLUSIVE,
449
PKGDB_LOCK_ADVISORY);
450
}
451
else {
452
rc = EXIT_FAILURE;
453
break;
454
}
455
}
456
i++;
457
} while (i < argc);
458
assert(it == NULL);
459
460
if (!verbose)
461
progressbar_stop();
462
xstring_free(msg);
463
vec_free_and_free(&dh, free);
464
pkg_free(pkg);
465
pkgdb_close(db);
466
467
return (rc);
468
}
469
470