| + |
#include <sys/param.h>
|
| + |
#include <sys/queue.h>
|
| + |
|
| + |
#include <err.h>
|
| + |
#include <assert.h>
|
| + |
#include <sysexits.h>
|
| + |
#include <stdbool.h>
|
| + |
#include <stdio.h>
|
| + |
#include <string.h>
|
| + |
#include <unistd.h>
|
| + |
#include <libutil.h>
|
| + |
|
| + |
#include <pkg.h>
|
| + |
|
| + |
#include "check.h"
|
| + |
#include "utils.h"
|
| + |
|
| + |
struct deps_entry {
|
| + |
char *name;
|
| + |
char *version;
|
| + |
char *origin;
|
| + |
STAILQ_ENTRY(deps_entry) next;
|
| + |
};
|
| + |
|
| + |
STAILQ_HEAD(deps_head, deps_entry);
|
| + |
|
| + |
static int check_deps(struct pkgdb *db, struct pkg *pkg, struct deps_head *dh);
|
| + |
static void add_missing_dep(struct pkg_dep *d, struct deps_head *dh);
|
| + |
static void deps_free(struct deps_head *dh);
|
| + |
static void fix_deps(struct pkgdb *db, struct deps_head *dh, int nbpkgs);
|
| + |
static void check_summary(struct pkgdb *db, struct deps_head *dh);
|
| + |
|
| + |
static int
|
| + |
check_deps(struct pkgdb *db, struct pkg *p, struct deps_head *dh)
|
| + |
{
|
| + |
struct pkg *pkg = NULL;
|
| + |
struct pkg_dep *dep = NULL;
|
| + |
struct pkgdb_it *it = NULL;
|
| + |
char *name, *version, *origin;
|
| + |
int nbpkgs = 0;
|
| + |
|
| + |
assert(db != NULL);
|
| + |
assert(p != NULL);
|
| + |
|
| + |
name = version = origin = NULL;
|
| + |
pkg_get(p, PKG_NAME, &name, PKG_VERSION, &version, PKG_ORIGIN, &origin);
|
| + |
|
| + |
while (pkg_deps(p, &dep) == EPKG_OK) {
|
| + |
if ((it = pkgdb_query(db, pkg_dep_get(dep, PKG_DEP_ORIGIN), MATCH_EXACT)) == NULL)
|
| + |
return (0);;
|
| + |
|
| + |
/* do we have a missing dependency? */
|
| + |
if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
|
| + |
printf("%s has a missing dependency: %s\n", origin,
|
| + |
pkg_dep_get(dep, PKG_DEP_ORIGIN)),
|
| + |
add_missing_dep(dep, dh);
|
| + |
nbpkgs++;
|
| + |
}
|
| + |
|
| + |
pkgdb_it_free(it);
|
| + |
}
|
| + |
|
| + |
pkg_free(pkg);
|
| + |
|
| + |
return (nbpkgs);
|
| + |
}
|
| + |
|
| + |
static void
|
| + |
add_missing_dep(struct pkg_dep *d, struct deps_head *dh)
|
| + |
{
|
| + |
struct deps_entry *e = NULL;
|
| + |
|
| + |
assert(d != NULL);
|
| + |
|
| + |
if ((e = calloc(1, sizeof(struct deps_entry))) == NULL)
|
| + |
err(1, "calloc(deps_entry)");
|
| + |
|
| + |
e->name = strdup(pkg_dep_get(d, PKG_DEP_NAME));
|
| + |
e->version = strdup(pkg_dep_get(d, PKG_DEP_VERSION));
|
| + |
e->origin = strdup(pkg_dep_get(d, PKG_DEP_ORIGIN));
|
| + |
|
| + |
STAILQ_INSERT_TAIL(dh, e, next);
|
| + |
}
|
| + |
|
| + |
static void
|
| + |
deps_free(struct deps_head *dh)
|
| + |
{
|
| + |
struct deps_entry *e = NULL;
|
| + |
|
| + |
while (!STAILQ_EMPTY(dh)) {
|
| + |
e = STAILQ_FIRST(dh);
|
| + |
STAILQ_REMOVE_HEAD(dh, next);
|
| + |
free(e->name);
|
| + |
free(e->version);
|
| + |
free(e->origin);
|
| + |
free(e);
|
| + |
}
|
| + |
}
|
| + |
|
| + |
static void
|
| + |
fix_deps(struct pkgdb *db, struct deps_head *dh, int nbpkgs)
|
| + |
{
|
| + |
struct pkg *pkg = NULL;
|
| + |
struct pkgdb_it *it = NULL;
|
| + |
struct pkg_jobs *jobs = NULL;
|
| + |
struct deps_entry *e = NULL;
|
| + |
char **pkgs = NULL;
|
| + |
int64_t dlsize = 0;
|
| + |
int64_t oldsize = 0, newsize = 0;
|
| + |
char size[7];
|
| + |
int i = 0;
|
| + |
|
| + |
assert(db != NULL);
|
| + |
assert(nbpkgs > 0);
|
| + |
|
| + |
if ((pkgs = calloc(nbpkgs, MAXPATHLEN + 1)) == NULL)
|
| + |
err(1, "calloc()");
|
| + |
|
| + |
STAILQ_FOREACH(e, dh, next)
|
| + |
pkgs[i++] = e->origin;
|
| + |
|
| + |
if (pkg_jobs_new(&jobs, PKG_JOBS_INSTALL, db) != EPKG_OK)
|
| + |
free(pkgs);
|
| + |
|
| + |
if ((it = pkgdb_query_installs(db, MATCH_EXACT, nbpkgs, pkgs, NULL)) == NULL) {
|
| + |
free(pkgs);
|
| + |
pkg_jobs_free(jobs);
|
| + |
}
|
| + |
|
| + |
while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_DEPS) == EPKG_OK) {
|
| + |
pkg_jobs_add(jobs, pkg);
|
| + |
pkg = NULL;
|
| + |
}
|
| + |
|
| + |
if (pkg_jobs_is_empty(jobs)) {
|
| + |
printf("\n>>> Not able to find packages for installation.\n\n");
|
| + |
return;
|
| + |
}
|
| + |
|
| + |
/* print a summary before applying the jobs */
|
| + |
pkg = NULL;
|
| + |
printf("The following packages will be installed:\n");
|
| + |
|
| + |
while (pkg_jobs(jobs, &pkg) == EPKG_OK) {
|
| + |
const char *name, *version, *newversion;
|
| + |
int64_t flatsize, newflatsize, pkgsize;
|
| + |
pkg_get(pkg, PKG_NEWVERSION, &newversion, PKG_NAME, &name,
|
| + |
PKG_VERSION, &version, PKG_FLATSIZE, &flatsize,
|
| + |
PKG_NEW_FLATSIZE, &newflatsize, PKG_NEW_PKGSIZE, &pkgsize);
|
| + |
dlsize += pkgsize;
|
| + |
if (newversion != NULL) {
|
| + |
printf("\tUpgrading %s: %s -> %s\n", name, version, newversion);
|
| + |
oldsize += flatsize;
|
| + |
newsize += flatsize;
|
| + |
} else {
|
| + |
newsize += flatsize;
|
| + |
printf("\tInstalling %s: %s\n", name, version);
|
| + |
}
|
| + |
}
|
| + |
|
| + |
if (oldsize > newsize) {
|
| + |
newsize *= -1;
|
| + |
humanize_number(size, sizeof(size), oldsize - newsize, "B", HN_AUTOSCALE, 0);
|
| + |
printf("\nthe installation will save %s\n", size);
|
| + |
} else {
|
| + |
humanize_number(size, sizeof(size), newsize - oldsize, "B", HN_AUTOSCALE, 0);
|
| + |
printf("\nthe installation will require %s more space\n", size);
|
| + |
}
|
| + |
humanize_number(size, sizeof(size), dlsize, "B", HN_AUTOSCALE, 0);
|
| + |
printf("%s to be downloaded\n", size);
|
| + |
|
| + |
pkg_jobs_apply(jobs, 0);
|
| + |
|
| + |
free(pkgs);
|
| + |
pkg_free(pkg);
|
| + |
pkg_jobs_free(jobs);
|
| + |
pkgdb_it_free(it);
|
| + |
}
|
| + |
|
| + |
static void
|
| + |
check_summary(struct pkgdb *db, struct deps_head *dh)
|
| + |
{
|
| + |
struct deps_entry *e = NULL;
|
| + |
struct pkg *pkg = NULL;
|
| + |
struct pkgdb_it *it = NULL;
|
| + |
bool fixed = true;
|
| + |
|
| + |
assert(db != NULL);
|
| + |
|
| + |
printf(">>> Summary of actions performed:\n\n");
|
| + |
|
| + |
STAILQ_FOREACH(e, dh, next) {
|
| + |
if ((it = pkgdb_query(db, e->origin, MATCH_EXACT)) == NULL)
|
| + |
return;
|
| + |
|
| + |
if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
|
| + |
fixed = false;
|
| + |
printf("%s dependency failed to be fixed\n", e->origin);
|
| + |
} else
|
| + |
printf("%s dependency has been fixed\n", e->origin);
|
| + |
|
| + |
pkgdb_it_free(it);
|
| + |
}
|
| + |
|
| + |
if (fixed) {
|
| + |
printf("\n>>> Missing dependencies were fixed successfully.\n");
|
| + |
} else {
|
| + |
printf("\n>>> There are still missing dependencies.\n");
|
| + |
printf(">>> You are advised to try fixing them manually.\n");
|
| + |
printf("\n>>> Also make sure to check 'pkg updating' for known issues.\n");
|
| + |
}
|
| + |
|
| + |
pkg_free(pkg);
|
| + |
}
|
| + |
|
| + |
void
|
| + |
usage_check(void)
|
| + |
{
|
| + |
fprintf(stderr, "usage: pkg check [-y]\n\n");
|
| + |
fprintf(stderr, "For more information see 'pkg help check'.\n");
|
| + |
}
|
| + |
|
| + |
int
|
| + |
exec_check(int argc, char **argv)
|
| + |
{
|
| + |
struct pkg *pkg = NULL;
|
| + |
struct pkgdb_it *it = NULL;
|
| + |
struct pkgdb *db = NULL;
|
| + |
int retcode = EX_OK;
|
| + |
int ch;
|
| + |
bool yes = false;
|
| + |
int nbpkgs = 0;
|
| + |
|
| + |
struct deps_head dh = STAILQ_HEAD_INITIALIZER(dh);
|
| + |
|
| + |
while ((ch = getopt(argc, argv, "y")) != -1) {
|
| + |
switch (ch) {
|
| + |
case 'y':
|
| + |
yes = true;
|
| + |
break;
|
| + |
default:
|
| + |
usage_check();
|
| + |
return (EX_USAGE);
|
| + |
}
|
| + |
}
|
| + |
argc -= optind;
|
| + |
argv += optind;
|
| + |
|
| + |
if (argc != 0) {
|
| + |
usage_check();
|
| + |
return (EX_USAGE);
|
| + |
}
|
| + |
|
| + |
if (geteuid() != 0) {
|
| + |
warnx("fixing package database can only be done as root");
|
| + |
return (EX_NOPERM);
|
| + |
}
|
| + |
|
| + |
if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) {
|
| + |
return (EX_IOERR);
|
| + |
}
|
| + |
|
| + |
if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
|
| + |
pkgdb_close(db);
|
| + |
return (EX_IOERR);
|
| + |
}
|
| + |
|
| + |
/* check for missing dependencies */
|
| + |
while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_DEPS) == EPKG_OK)
|
| + |
nbpkgs += check_deps(db, pkg, &dh);
|
| + |
|
| + |
if (nbpkgs > 0) {
|
| + |
printf("\n>>> Missing package dependencies were detected.\n");
|
| + |
|
| + |
if (yes == false)
|
| + |
pkg_config_bool(PKG_CONFIG_ASSUME_ALWAYS_YES, &yes);
|
| + |
if (yes == false)
|
| + |
yes = query_yesno("\nTry to fix the missing dependencies [y/N]: ");
|
| + |
|
| + |
if (yes == true) {
|
| + |
fix_deps(db, &dh, nbpkgs);
|
| + |
check_summary(db, &dh);
|
| + |
}
|
| + |
}
|
| + |
|
| + |
deps_free(&dh);
|
| + |
pkg_free(pkg);
|
| + |
pkgdb_it_free(it);
|
| + |
pkgdb_close(db);
|
| + |
|
| + |
return (retcode);
|
| + |
}
|