Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/check.c
2645 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 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
}
476
477