2013-06-11 11:06:06 +00:00
|
|
|
/* commit.c - Alpine Package Keeper (APK)
|
|
|
|
* Apply solver calculated changes to database.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008-2013 Timo Teräs <timo.teras@iki.fi>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2020-04-22 13:33:41 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
2013-06-11 11:06:06 +00:00
|
|
|
*/
|
|
|
|
|
2020-10-07 18:16:13 +00:00
|
|
|
#include <assert.h>
|
2017-03-06 16:38:32 +00:00
|
|
|
#include <limits.h>
|
2013-06-11 11:06:06 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "apk_defines.h"
|
|
|
|
#include "apk_database.h"
|
|
|
|
#include "apk_package.h"
|
|
|
|
#include "apk_solver.h"
|
|
|
|
|
|
|
|
#include "apk_print.h"
|
|
|
|
|
|
|
|
static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
|
|
|
|
{
|
|
|
|
if (pkg->repos & db->available_repos)
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int print_change(struct apk_database *db, struct apk_change *change,
|
|
|
|
int cur, int total)
|
|
|
|
{
|
|
|
|
struct apk_name *name;
|
|
|
|
struct apk_package *oldpkg = change->old_pkg;
|
|
|
|
struct apk_package *newpkg = change->new_pkg;
|
|
|
|
const char *msg = NULL;
|
2013-06-18 11:30:44 +00:00
|
|
|
char status[32];
|
2013-06-11 11:06:06 +00:00
|
|
|
apk_blob_t *oneversion = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
snprintf(status, sizeof(status), "(%i/%i)", cur+1, total);
|
|
|
|
|
|
|
|
name = newpkg ? newpkg->name : oldpkg->name;
|
|
|
|
if (oldpkg == NULL) {
|
|
|
|
msg = "Installing";
|
|
|
|
oneversion = newpkg->version;
|
|
|
|
} else if (newpkg == NULL) {
|
|
|
|
msg = "Purging";
|
|
|
|
oneversion = oldpkg->version;
|
|
|
|
} else if (newpkg == oldpkg) {
|
|
|
|
if (change->reinstall) {
|
|
|
|
if (pkg_available(db, newpkg))
|
2013-06-20 10:12:44 +00:00
|
|
|
msg = "Reinstalling";
|
2013-06-11 11:06:06 +00:00
|
|
|
else
|
2013-06-21 08:32:23 +00:00
|
|
|
msg = "[APK unavailable, skipped] Reinstalling";
|
2013-06-11 11:06:06 +00:00
|
|
|
} else if (change->old_repository_tag != change->new_repository_tag) {
|
|
|
|
msg = "Updating pinning";
|
|
|
|
}
|
|
|
|
oneversion = newpkg->version;
|
|
|
|
} else {
|
|
|
|
r = apk_pkg_version_compare(newpkg, oldpkg);
|
|
|
|
switch (r) {
|
|
|
|
case APK_VERSION_LESS:
|
|
|
|
msg = "Downgrading";
|
|
|
|
break;
|
|
|
|
case APK_VERSION_EQUAL:
|
|
|
|
msg = "Replacing";
|
|
|
|
break;
|
|
|
|
case APK_VERSION_GREATER:
|
|
|
|
msg = "Upgrading";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msg == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (oneversion) {
|
2013-06-18 11:30:44 +00:00
|
|
|
apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT ")",
|
|
|
|
status, msg,
|
|
|
|
name->name,
|
|
|
|
BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
|
2013-06-11 11:06:06 +00:00
|
|
|
BLOB_PRINTF(*oneversion));
|
|
|
|
} else {
|
2013-06-18 11:30:44 +00:00
|
|
|
apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT " -> " BLOB_FMT ")",
|
|
|
|
status, msg,
|
|
|
|
name->name,
|
|
|
|
BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
|
2013-06-11 11:06:06 +00:00
|
|
|
BLOB_PRINTF(*oldpkg->version),
|
|
|
|
BLOB_PRINTF(*newpkg->version));
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct apk_stats {
|
|
|
|
unsigned int changes;
|
2013-06-19 08:47:09 +00:00
|
|
|
size_t bytes;
|
2013-06-11 11:06:06 +00:00
|
|
|
unsigned int packages;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void count_change(struct apk_change *change, struct apk_stats *stats)
|
|
|
|
{
|
2013-06-12 05:45:29 +00:00
|
|
|
if (change->new_pkg != change->old_pkg || change->reinstall) {
|
2013-06-11 11:06:06 +00:00
|
|
|
if (change->new_pkg != NULL) {
|
|
|
|
stats->bytes += change->new_pkg->installed_size;
|
2013-06-12 05:45:29 +00:00
|
|
|
stats->packages++;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
if (change->old_pkg != NULL)
|
2013-06-12 05:45:29 +00:00
|
|
|
stats->packages++;
|
2013-06-11 11:06:06 +00:00
|
|
|
stats->changes++;
|
2013-06-12 05:45:29 +00:00
|
|
|
} else if (change->new_repository_tag != change->old_repository_tag) {
|
|
|
|
stats->packages++;
|
2013-06-11 11:06:06 +00:00
|
|
|
stats->changes++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct progress {
|
|
|
|
struct apk_stats done;
|
|
|
|
struct apk_stats total;
|
|
|
|
struct apk_package *pkg;
|
|
|
|
};
|
|
|
|
|
2013-06-17 14:28:03 +00:00
|
|
|
static void progress_cb(void *ctx, size_t installed_bytes)
|
2013-06-11 11:06:06 +00:00
|
|
|
{
|
|
|
|
struct progress *prog = (struct progress *) ctx;
|
2013-06-19 08:47:09 +00:00
|
|
|
apk_print_progress(prog->done.bytes + prog->done.packages + installed_bytes,
|
|
|
|
prog->total.bytes + prog->total.packages);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_packages(struct apk_changeset *changeset,
|
|
|
|
int (*cmp)(struct apk_change *change),
|
|
|
|
const char *msg)
|
|
|
|
{
|
|
|
|
struct apk_change *change;
|
|
|
|
struct apk_name *name;
|
|
|
|
struct apk_indent indent = { .indent = 2 };
|
2013-06-18 05:03:40 +00:00
|
|
|
int match = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-18 05:03:40 +00:00
|
|
|
foreach_array_item(change, changeset->changes) {
|
2013-06-11 11:06:06 +00:00
|
|
|
if (!cmp(change))
|
|
|
|
continue;
|
|
|
|
if (match == 0)
|
|
|
|
printf("%s:\n", msg);
|
|
|
|
if (change->new_pkg != NULL)
|
|
|
|
name = change->new_pkg->name;
|
|
|
|
else
|
|
|
|
name = change->old_pkg->name;
|
|
|
|
|
|
|
|
apk_print_indented(&indent, APK_BLOB_STR(name->name));
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
if (match)
|
|
|
|
printf("\n");
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_remove(struct apk_change *change)
|
|
|
|
{
|
|
|
|
return change->new_pkg == NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_new(struct apk_change *change)
|
|
|
|
{
|
|
|
|
return change->old_pkg == NULL;
|
|
|
|
}
|
|
|
|
|
2013-06-20 10:12:44 +00:00
|
|
|
static int cmp_reinstall(struct apk_change *change)
|
|
|
|
{
|
|
|
|
return change->reinstall;
|
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
static int cmp_downgrade(struct apk_change *change)
|
|
|
|
{
|
|
|
|
if (change->new_pkg == NULL || change->old_pkg == NULL)
|
|
|
|
return 0;
|
|
|
|
if (apk_pkg_version_compare(change->new_pkg, change->old_pkg)
|
|
|
|
& APK_VERSION_LESS)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_upgrade(struct apk_change *change)
|
|
|
|
{
|
|
|
|
if (change->new_pkg == NULL || change->old_pkg == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Count swapping package as upgrade too - this can happen if
|
|
|
|
* same package version is used after it was rebuilt against
|
|
|
|
* newer libraries. Basically, different (and probably newer)
|
|
|
|
* package, but equal version number. */
|
|
|
|
if ((apk_pkg_version_compare(change->new_pkg, change->old_pkg) &
|
|
|
|
(APK_VERSION_GREATER | APK_VERSION_EQUAL)) &&
|
|
|
|
(change->new_pkg != change->old_pkg))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void run_triggers(struct apk_database *db, struct apk_changeset *changeset)
|
|
|
|
{
|
2013-06-18 05:03:40 +00:00
|
|
|
struct apk_change *change;
|
|
|
|
struct apk_installed_package *ipkg;
|
2013-06-11 11:06:06 +00:00
|
|
|
|
|
|
|
if (apk_db_fire_triggers(db) == 0)
|
|
|
|
return;
|
|
|
|
|
2013-06-18 05:03:40 +00:00
|
|
|
foreach_array_item(change, changeset->changes) {
|
|
|
|
struct apk_package *pkg = change->new_pkg;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (pkg == NULL)
|
|
|
|
continue;
|
|
|
|
ipkg = pkg->ipkg;
|
2013-08-05 08:56:28 +00:00
|
|
|
if (ipkg == NULL || ipkg->pending_triggers->num == 0)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
*apk_string_array_add(&ipkg->pending_triggers) = NULL;
|
|
|
|
apk_ipkg_run_script(ipkg, db, APK_SCRIPT_TRIGGER,
|
|
|
|
ipkg->pending_triggers->item);
|
|
|
|
apk_string_array_free(&ipkg->pending_triggers);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-02 23:37:23 +00:00
|
|
|
#define PRE_COMMIT_HOOK 0
|
|
|
|
#define POST_COMMIT_HOOK 1
|
|
|
|
|
|
|
|
struct apk_commit_hook {
|
|
|
|
struct apk_database *db;
|
|
|
|
int type;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int run_commit_hook(void *ctx, int dirfd, const char *file)
|
|
|
|
{
|
|
|
|
static char *const commit_hook_str[] = { "pre-commit", "post-commit" };
|
|
|
|
struct apk_commit_hook *hook = (struct apk_commit_hook *) ctx;
|
|
|
|
struct apk_database *db = hook->db;
|
|
|
|
char fn[PATH_MAX], *argv[] = { fn, (char *) commit_hook_str[hook->type], NULL };
|
|
|
|
|
2018-09-05 16:49:22 +00:00
|
|
|
if (file[0] == '.') return 0;
|
|
|
|
if ((apk_flags & (APK_NO_SCRIPTS | APK_SIMULATE)) != 0) return 0;
|
2017-02-02 23:37:23 +00:00
|
|
|
|
|
|
|
snprintf(fn, sizeof(fn), "etc/apk/commit_hooks.d" "/%s", file);
|
2017-12-29 20:10:44 +00:00
|
|
|
if ((apk_flags & APK_NO_COMMIT_HOOKS) != 0) {
|
|
|
|
apk_message("Skipping: %s %s", fn, commit_hook_str[hook->type]);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-02-15 22:00:04 +00:00
|
|
|
if (apk_verbosity >= 2) apk_message("Executing: %s %s", fn, commit_hook_str[hook->type]);
|
2017-02-02 23:37:23 +00:00
|
|
|
|
|
|
|
if (apk_db_run_script(db, fn, argv) < 0 && hook->type == PRE_COMMIT_HOOK)
|
2017-02-27 08:54:28 +00:00
|
|
|
return -2;
|
2017-02-02 23:37:23 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int run_commit_hooks(struct apk_database *db, int type)
|
|
|
|
{
|
|
|
|
struct apk_commit_hook hook = { .db = db, .type = type };
|
|
|
|
return apk_dir_foreach_file(openat(db->root_fd, "etc/apk/commit_hooks.d", O_RDONLY | O_CLOEXEC),
|
|
|
|
run_commit_hook, &hook);
|
|
|
|
}
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
int apk_solver_commit_changeset(struct apk_database *db,
|
|
|
|
struct apk_changeset *changeset,
|
|
|
|
struct apk_dependency_array *world)
|
|
|
|
{
|
|
|
|
struct progress prog;
|
|
|
|
struct apk_change *change;
|
2019-06-05 13:01:35 +00:00
|
|
|
char buf[32];
|
|
|
|
const char *size_unit;
|
|
|
|
off_t humanized, size_diff = 0;
|
2013-06-20 10:12:44 +00:00
|
|
|
int r, errors = 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2020-10-07 18:16:13 +00:00
|
|
|
assert(world);
|
2013-06-11 11:06:06 +00:00
|
|
|
if (apk_db_check_world(db, world) != 0) {
|
2018-01-03 13:17:11 +00:00
|
|
|
apk_error("Not committing changes due to missing repository tags. "
|
|
|
|
"Use --force-broken-world to override.");
|
2013-06-11 11:06:06 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changeset->changes == NULL)
|
|
|
|
goto all_done;
|
|
|
|
|
|
|
|
/* Count what needs to be done */
|
|
|
|
memset(&prog, 0, sizeof(prog));
|
2013-06-18 05:03:40 +00:00
|
|
|
foreach_array_item(change, changeset->changes) {
|
2013-06-11 11:06:06 +00:00
|
|
|
count_change(change, &prog.total);
|
|
|
|
if (change->new_pkg)
|
2019-06-05 13:01:35 +00:00
|
|
|
size_diff += change->new_pkg->installed_size;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (change->old_pkg)
|
2019-06-05 13:01:35 +00:00
|
|
|
size_diff -= change->old_pkg->installed_size;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2019-06-05 13:01:35 +00:00
|
|
|
size_unit = apk_get_human_size(llabs(size_diff), &humanized);
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-08-03 14:31:49 +00:00
|
|
|
if ((apk_verbosity > 1 || (apk_flags & APK_INTERACTIVE)) &&
|
|
|
|
!(apk_flags & APK_SIMULATE)) {
|
2013-06-11 11:06:06 +00:00
|
|
|
r = dump_packages(changeset, cmp_remove,
|
|
|
|
"The following packages will be REMOVED");
|
|
|
|
r += dump_packages(changeset, cmp_downgrade,
|
|
|
|
"The following packages will be DOWNGRADED");
|
|
|
|
if (r || (apk_flags & APK_INTERACTIVE) || apk_verbosity > 2) {
|
2013-08-03 14:31:49 +00:00
|
|
|
r += dump_packages(changeset, cmp_new,
|
|
|
|
"The following NEW packages will be installed");
|
|
|
|
r += dump_packages(changeset, cmp_upgrade,
|
|
|
|
"The following packages will be upgraded");
|
|
|
|
r += dump_packages(changeset, cmp_reinstall,
|
|
|
|
"The following packages will be reinstalled");
|
2019-06-05 13:01:35 +00:00
|
|
|
printf("After this operation, %lld %s of %s.\n",
|
|
|
|
(long long)humanized,
|
2013-06-20 12:56:04 +00:00
|
|
|
size_unit,
|
2013-06-11 11:06:06 +00:00
|
|
|
(size_diff < 0) ?
|
2013-06-20 12:56:04 +00:00
|
|
|
"disk space will be freed" :
|
|
|
|
"additional disk space will be used");
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2013-08-03 14:31:49 +00:00
|
|
|
if (r > 0 && (apk_flags & APK_INTERACTIVE)) {
|
2013-06-11 11:06:06 +00:00
|
|
|
printf("Do you want to continue [Y/n]? ");
|
|
|
|
fflush(stdout);
|
|
|
|
r = fgetc(stdin);
|
|
|
|
if (r != 'y' && r != 'Y' && r != '\n')
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-27 08:54:28 +00:00
|
|
|
if (run_commit_hooks(db, PRE_COMMIT_HOOK) == -2)
|
2017-02-02 23:37:23 +00:00
|
|
|
return -1;
|
|
|
|
|
2013-06-11 11:06:06 +00:00
|
|
|
/* Go through changes */
|
2013-06-18 05:03:40 +00:00
|
|
|
foreach_array_item(change, changeset->changes) {
|
2013-06-20 10:12:44 +00:00
|
|
|
r = change->old_pkg &&
|
|
|
|
(change->old_pkg->ipkg->broken_files ||
|
|
|
|
change->old_pkg->ipkg->broken_script);
|
2013-06-11 11:06:06 +00:00
|
|
|
if (print_change(db, change, prog.done.changes, prog.total.changes)) {
|
|
|
|
prog.pkg = change->new_pkg;
|
|
|
|
progress_cb(&prog, 0);
|
|
|
|
|
2013-06-20 10:12:44 +00:00
|
|
|
if (!(apk_flags & APK_SIMULATE) &&
|
|
|
|
((change->old_pkg != change->new_pkg) ||
|
|
|
|
(change->reinstall && pkg_available(db, change->new_pkg)))) {
|
|
|
|
r = apk_db_install_pkg(db, change->old_pkg, change->new_pkg,
|
|
|
|
progress_cb, &prog) != 0;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2013-06-20 10:12:44 +00:00
|
|
|
if (r == 0 && change->new_pkg && change->new_pkg->ipkg)
|
|
|
|
change->new_pkg->ipkg->repository_tag = change->new_repository_tag;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2013-06-20 10:12:44 +00:00
|
|
|
errors += r;
|
2013-06-11 11:06:06 +00:00
|
|
|
count_change(change, &prog.done);
|
|
|
|
}
|
2013-06-19 08:47:09 +00:00
|
|
|
apk_print_progress(prog.total.bytes + prog.total.packages,
|
|
|
|
prog.total.bytes + prog.total.packages);
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2014-10-07 10:29:34 +00:00
|
|
|
apk_db_update_directory_permissions(db);
|
2013-06-11 11:06:06 +00:00
|
|
|
run_triggers(db, changeset);
|
|
|
|
|
|
|
|
all_done:
|
|
|
|
apk_dependency_array_copy(&db->world, world);
|
|
|
|
apk_db_write_config(db);
|
2017-02-02 23:37:23 +00:00
|
|
|
run_commit_hooks(db, POST_COMMIT_HOOK);
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2016-07-22 08:13:33 +00:00
|
|
|
if (!db->performing_self_upgrade) {
|
2013-06-20 10:12:44 +00:00
|
|
|
if (errors)
|
2017-08-23 06:02:23 +00:00
|
|
|
snprintf(buf, sizeof(buf), "%d error%s;", errors,
|
|
|
|
errors > 1 ? "s" : "");
|
2013-06-20 10:12:44 +00:00
|
|
|
else
|
|
|
|
strcpy(buf, "OK:");
|
2013-06-11 11:06:06 +00:00
|
|
|
if (apk_verbosity > 1) {
|
2013-06-20 10:12:44 +00:00
|
|
|
apk_message("%s %d packages, %d dirs, %d files, %zu MiB",
|
|
|
|
buf,
|
2013-06-11 11:06:06 +00:00
|
|
|
db->installed.stats.packages,
|
|
|
|
db->installed.stats.dirs,
|
|
|
|
db->installed.stats.files,
|
|
|
|
db->installed.stats.bytes / (1024 * 1024));
|
|
|
|
} else {
|
2013-06-20 10:12:44 +00:00
|
|
|
apk_message("%s %zu MiB in %d packages",
|
|
|
|
buf,
|
2013-06-11 11:06:06 +00:00
|
|
|
db->installed.stats.bytes / (1024 * 1024),
|
|
|
|
db->installed.stats.packages);
|
|
|
|
}
|
|
|
|
}
|
2015-04-07 08:18:13 +00:00
|
|
|
return errors;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
enum {
|
2013-06-18 05:03:40 +00:00
|
|
|
STATE_PRESENT = 0x80000000,
|
|
|
|
STATE_MISSING = 0x40000000,
|
|
|
|
STATE_COUNT_MASK = 0x0000ffff,
|
2013-06-13 15:20:39 +00:00
|
|
|
};
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
struct print_state {
|
|
|
|
struct apk_database *db;
|
|
|
|
struct apk_dependency_array *world;
|
|
|
|
struct apk_indent i;
|
|
|
|
struct apk_name_array *missing;
|
|
|
|
const char *label;
|
|
|
|
int num_labels;
|
|
|
|
int match;
|
|
|
|
};
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
static void label_start(struct print_state *ps, const char *text)
|
|
|
|
{
|
|
|
|
if (ps->label) {
|
|
|
|
printf(" %s:\n", ps->label);
|
|
|
|
ps->label = NULL;
|
|
|
|
ps->i.x = ps->i.indent = 0;
|
|
|
|
ps->num_labels++;
|
|
|
|
}
|
|
|
|
if (ps->i.x == 0) {
|
|
|
|
ps->i.x = printf(" %s", text);
|
|
|
|
ps->i.indent = ps->i.x + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void label_end(struct print_state *ps)
|
|
|
|
{
|
|
|
|
if (ps->i.x != 0) {
|
|
|
|
printf("\n");
|
|
|
|
ps->i.x = ps->i.indent = 0;
|
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
|
2013-06-11 11:06:06 +00:00
|
|
|
{
|
2013-06-13 15:20:39 +00:00
|
|
|
struct apk_database *db = ps->db;
|
2013-06-11 11:06:06 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (pkg->ipkg != NULL)
|
|
|
|
return;
|
|
|
|
|
2013-06-18 13:24:48 +00:00
|
|
|
if (!(pkg->repos & db->available_repos)) {
|
|
|
|
label_start(ps, "masked in:");
|
|
|
|
apk_print_indented_fmt(&ps->i, "--no-network");
|
2018-11-02 15:12:20 +00:00
|
|
|
} else if (pkg->repos == BIT(APK_REPOSITORY_CACHED) && !(pkg->filename != NULL || pkg->installed_size == 0)) {
|
2014-05-19 08:50:10 +00:00
|
|
|
label_start(ps, "masked in:");
|
|
|
|
apk_print_indented_fmt(&ps->i, "cache");
|
2013-06-18 13:24:48 +00:00
|
|
|
} else {
|
|
|
|
if (pkg->repos & apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK | BIT(tag)))
|
|
|
|
return;
|
|
|
|
for (i = 0; i < db->num_repo_tags; i++) {
|
|
|
|
if (pkg->repos & db->repo_tags[i].allowed_repos) {
|
|
|
|
label_start(ps, "masked in:");
|
|
|
|
apk_print_indented(&ps->i, db->repo_tags[i].tag);
|
|
|
|
}
|
2013-06-13 15:20:39 +00:00
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2013-06-13 15:20:39 +00:00
|
|
|
label_end(ps);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
static void print_conflicts(struct print_state *ps, struct apk_package *pkg)
|
2013-06-11 11:06:06 +00:00
|
|
|
{
|
2013-06-13 15:20:39 +00:00
|
|
|
struct apk_provider *p;
|
|
|
|
struct apk_dependency *d;
|
2013-06-14 18:37:38 +00:00
|
|
|
char tmp[256];
|
2015-12-10 12:30:57 +00:00
|
|
|
int once;
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
foreach_array_item(p, pkg->name->providers) {
|
2013-06-13 17:48:38 +00:00
|
|
|
if (p->pkg == pkg || !p->pkg->marked)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
2013-06-13 15:20:39 +00:00
|
|
|
label_start(ps, "conflicts:");
|
|
|
|
apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p->pkg));
|
|
|
|
}
|
|
|
|
foreach_array_item(d, pkg->provides) {
|
2015-12-10 12:30:57 +00:00
|
|
|
once = 1;
|
2013-06-13 15:20:39 +00:00
|
|
|
foreach_array_item(p, d->name->providers) {
|
2013-06-14 18:37:38 +00:00
|
|
|
if (!p->pkg->marked)
|
|
|
|
continue;
|
2020-05-19 08:39:21 +00:00
|
|
|
if (d->version == &apk_atom_null &&
|
|
|
|
p->version == &apk_atom_null)
|
2013-06-13 15:20:39 +00:00
|
|
|
continue;
|
2018-01-02 13:35:24 +00:00
|
|
|
if (once && p->pkg == pkg &&
|
|
|
|
p->version == d->version) {
|
2015-12-10 12:30:57 +00:00
|
|
|
once = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2013-06-13 15:20:39 +00:00
|
|
|
label_start(ps, "conflicts:");
|
|
|
|
apk_print_indented_fmt(
|
2013-06-14 18:37:38 +00:00
|
|
|
&ps->i, PKG_VER_FMT "[%s]",
|
2013-06-13 15:20:39 +00:00
|
|
|
PKG_VER_PRINTF(p->pkg),
|
2013-06-14 18:37:38 +00:00
|
|
|
apk_dep_snprintf(tmp, sizeof(tmp), d));
|
2013-06-13 15:20:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
label_end(ps);
|
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 17:48:38 +00:00
|
|
|
static void print_dep(struct apk_package *pkg0, struct apk_dependency *d0, struct apk_package *pkg, void *ctx)
|
2013-06-13 15:20:39 +00:00
|
|
|
{
|
|
|
|
struct print_state *ps = (struct print_state *) ctx;
|
2013-06-13 17:48:38 +00:00
|
|
|
const char *label = (ps->match & APK_DEP_SATISFIES) ? "satisfies:" : "breaks:";
|
2013-06-13 15:20:39 +00:00
|
|
|
char tmp[256];
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
label_start(ps, label);
|
|
|
|
if (pkg0 == NULL)
|
|
|
|
apk_print_indented_fmt(&ps->i, "world[%s]", apk_dep_snprintf(tmp, sizeof(tmp), d0));
|
|
|
|
else
|
|
|
|
apk_print_indented_fmt(&ps->i, PKG_VER_FMT "[%s]",
|
|
|
|
PKG_VER_PRINTF(pkg0),
|
|
|
|
apk_dep_snprintf(tmp, sizeof(tmp), d0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_deps(struct print_state *ps, struct apk_package *pkg, int match)
|
|
|
|
{
|
|
|
|
ps->match = match;
|
2015-12-10 12:30:57 +00:00
|
|
|
match |= APK_FOREACH_MARKED | APK_FOREACH_DEP;
|
|
|
|
apk_pkg_foreach_matching_dependency(NULL, ps->world, match|apk_foreach_genid(), pkg, print_dep, ps);
|
|
|
|
apk_pkg_foreach_reverse_dependency(pkg, match|apk_foreach_genid(), print_dep, ps);
|
2013-06-13 15:20:39 +00:00
|
|
|
label_end(ps);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
|
2013-06-11 11:06:06 +00:00
|
|
|
{
|
2013-06-13 15:20:39 +00:00
|
|
|
char pkgtext[256];
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
|
|
|
|
ps->label = pkgtext;
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
print_pinning_errors(ps, pkg, tag);
|
|
|
|
print_conflicts(ps, pkg);
|
2015-12-10 12:30:57 +00:00
|
|
|
print_deps(ps, pkg, APK_DEP_CONFLICTS);
|
2013-06-13 15:20:39 +00:00
|
|
|
if (ps->label == NULL)
|
2015-12-10 12:30:57 +00:00
|
|
|
print_deps(ps, pkg, APK_DEP_SATISFIES);
|
2013-06-13 15:20:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void analyze_name(struct print_state *ps, struct apk_name *name)
|
|
|
|
{
|
|
|
|
struct apk_name **pname0, *name0;
|
|
|
|
struct apk_provider *p0;
|
|
|
|
struct apk_dependency *d0;
|
|
|
|
char tmp[256];
|
2018-11-02 13:34:59 +00:00
|
|
|
unsigned int genid;
|
2013-06-18 05:03:40 +00:00
|
|
|
int refs;
|
2013-06-13 15:20:39 +00:00
|
|
|
|
|
|
|
if (name->providers->num) {
|
|
|
|
snprintf(tmp, sizeof(tmp), "%s (virtual)", name->name);
|
|
|
|
ps->label = tmp;
|
|
|
|
|
|
|
|
label_start(ps, "provided by:");
|
2013-06-18 05:03:40 +00:00
|
|
|
foreach_array_item(p0, name->providers)
|
|
|
|
p0->pkg->name->state_int++;
|
2013-06-13 15:20:39 +00:00
|
|
|
foreach_array_item(p0, name->providers) {
|
2013-06-18 05:03:40 +00:00
|
|
|
name0 = p0->pkg->name;
|
|
|
|
refs = (name0->state_int & STATE_COUNT_MASK);
|
|
|
|
if (refs == name0->providers->num) {
|
|
|
|
/* name only */
|
|
|
|
apk_print_indented(&ps->i, APK_BLOB_STR(name0->name));
|
|
|
|
name0->state_int &= ~STATE_COUNT_MASK;
|
|
|
|
} else if (refs > 0) {
|
|
|
|
/* individual package */
|
|
|
|
apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p0->pkg));
|
|
|
|
name0->state_int--;
|
|
|
|
}
|
2013-06-13 15:20:39 +00:00
|
|
|
}
|
|
|
|
label_end(ps);
|
|
|
|
} else {
|
2020-10-03 10:57:05 +00:00
|
|
|
snprintf(tmp, sizeof(tmp), "%s (no such package)", name->name);
|
2013-06-13 15:20:39 +00:00
|
|
|
ps->label = tmp;
|
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
label_start(ps, "required by:");
|
|
|
|
foreach_array_item(d0, ps->world) {
|
|
|
|
if (d0->name != name || d0->conflict)
|
2013-06-11 11:06:06 +00:00
|
|
|
continue;
|
2013-06-13 15:20:39 +00:00
|
|
|
apk_print_indented_fmt(&ps->i, "world[%s]",
|
|
|
|
apk_dep_snprintf(tmp, sizeof(tmp), d0));
|
|
|
|
}
|
2018-11-02 13:34:59 +00:00
|
|
|
genid = apk_foreach_genid();
|
2013-06-13 15:20:39 +00:00
|
|
|
foreach_array_item(pname0, name->rdepends) {
|
|
|
|
name0 = *pname0;
|
|
|
|
foreach_array_item(p0, name0->providers) {
|
2013-06-13 17:48:38 +00:00
|
|
|
if (!p0->pkg->marked)
|
2013-06-13 15:20:39 +00:00
|
|
|
continue;
|
2018-11-02 13:34:59 +00:00
|
|
|
if (p0->pkg->foreach_genid == genid)
|
|
|
|
continue;
|
|
|
|
p0->pkg->foreach_genid = genid;
|
2013-06-13 15:20:39 +00:00
|
|
|
foreach_array_item(d0, p0->pkg->depends) {
|
|
|
|
if (d0->name != name || d0->conflict)
|
|
|
|
continue;
|
|
|
|
apk_print_indented_fmt(&ps->i,
|
|
|
|
PKG_VER_FMT "[%s]",
|
|
|
|
PKG_VER_PRINTF(p0->pkg),
|
|
|
|
apk_dep_snprintf(tmp, sizeof(tmp), d0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (d0 != NULL)
|
|
|
|
break;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
2013-06-13 15:20:39 +00:00
|
|
|
}
|
|
|
|
label_end(ps);
|
|
|
|
}
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
static void analyze_deps(struct print_state *ps, struct apk_dependency_array *deps)
|
|
|
|
{
|
|
|
|
struct apk_dependency *d0;
|
2013-06-18 05:03:40 +00:00
|
|
|
struct apk_name *name0;
|
2013-06-13 15:20:39 +00:00
|
|
|
|
|
|
|
foreach_array_item(d0, deps) {
|
2013-06-18 05:03:40 +00:00
|
|
|
name0 = d0->name;
|
2013-06-18 13:23:14 +00:00
|
|
|
if (d0->conflict)
|
|
|
|
continue;
|
2013-06-18 05:03:40 +00:00
|
|
|
if ((name0->state_int & (STATE_PRESENT | STATE_MISSING)) != 0)
|
2013-06-13 15:20:39 +00:00
|
|
|
continue;
|
2013-06-18 05:03:40 +00:00
|
|
|
name0->state_int |= STATE_MISSING;
|
|
|
|
analyze_name(ps, name0);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_solver_print_errors(struct apk_database *db,
|
|
|
|
struct apk_changeset *changeset,
|
|
|
|
struct apk_dependency_array *world)
|
|
|
|
{
|
2013-06-13 15:20:39 +00:00
|
|
|
struct print_state ps;
|
|
|
|
struct apk_change *change;
|
|
|
|
struct apk_dependency *p;
|
|
|
|
|
|
|
|
/* ERROR: unsatisfiable dependencies:
|
|
|
|
* name:
|
|
|
|
* required by: a b c d e
|
|
|
|
* not available in any repository
|
|
|
|
* name (virtual):
|
|
|
|
* required by: a b c d e
|
|
|
|
* provided by: foo bar zed
|
|
|
|
* pkg-1.2:
|
|
|
|
* masked by: @testing
|
|
|
|
* satisfies: a[pkg]
|
|
|
|
* conflicts: pkg-2.0 foo-1.2 bar-1.2
|
|
|
|
* breaks: b[pkg>2] c[foo>2] d[!pkg]
|
|
|
|
*
|
|
|
|
* When two packages provide same name 'foo':
|
|
|
|
* a-1:
|
|
|
|
* satisfies: world[a]
|
|
|
|
* conflicts: b-1[foo]
|
|
|
|
* b-1:
|
|
|
|
* satisfies: world[b]
|
|
|
|
* conflicts: a-1[foo]
|
|
|
|
*
|
|
|
|
* c-1:
|
|
|
|
* satisfies: world[a]
|
|
|
|
* conflicts: c-1[foo] (self-conflict by providing foo twice)
|
|
|
|
*
|
|
|
|
* When two packages get pulled in:
|
|
|
|
* a-1:
|
|
|
|
* satisfies: app1[so:a.so.1]
|
|
|
|
* conflicts: a-2
|
|
|
|
* a-2:
|
|
|
|
* satisfies: app2[so:a.so.2]
|
|
|
|
* conflicts: a-1
|
|
|
|
*
|
|
|
|
* satisfies lists all dependencies that is not satisfiable by
|
|
|
|
* any other selected version. or all of them with -v.
|
2013-06-11 11:06:06 +00:00
|
|
|
*/
|
2013-06-13 15:20:39 +00:00
|
|
|
|
2020-10-03 10:57:05 +00:00
|
|
|
apk_error("unable to select packages:");
|
2013-06-11 11:06:06 +00:00
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
/* Construct information about names */
|
|
|
|
foreach_array_item(change, changeset->changes) {
|
|
|
|
struct apk_package *pkg = change->new_pkg;
|
2013-06-11 11:06:06 +00:00
|
|
|
if (pkg == NULL)
|
|
|
|
continue;
|
2013-06-13 17:48:38 +00:00
|
|
|
pkg->marked = 1;
|
2013-06-18 05:03:40 +00:00
|
|
|
pkg->name->state_int |= STATE_PRESENT;
|
2013-06-13 15:20:39 +00:00
|
|
|
foreach_array_item(p, pkg->provides)
|
2013-06-18 05:03:40 +00:00
|
|
|
p->name->state_int |= STATE_PRESENT;
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
/* Analyze is package, and missing names referred to */
|
|
|
|
ps = (struct print_state) {
|
|
|
|
.db = db,
|
|
|
|
.world = world,
|
|
|
|
};
|
|
|
|
analyze_deps(&ps, world);
|
|
|
|
foreach_array_item(change, changeset->changes) {
|
2013-06-11 11:06:06 +00:00
|
|
|
struct apk_package *pkg = change->new_pkg;
|
|
|
|
if (pkg == NULL)
|
|
|
|
continue;
|
2013-06-13 15:20:39 +00:00
|
|
|
analyze_package(&ps, pkg, change->new_repository_tag);
|
|
|
|
analyze_deps(&ps, pkg->depends);
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
if (ps.num_labels == 0)
|
|
|
|
printf(" Huh? Error reporter did not find the broken constraints.\n");
|
2013-06-11 11:06:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int apk_solver_commit(struct apk_database *db,
|
|
|
|
unsigned short solver_flags,
|
|
|
|
struct apk_dependency_array *world)
|
|
|
|
{
|
|
|
|
struct apk_changeset changeset = {};
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (apk_db_check_world(db, world) != 0) {
|
2018-01-03 13:17:11 +00:00
|
|
|
apk_error("Not committing changes due to missing repository tags. "
|
|
|
|
"Use --force-broken-world to override.");
|
2013-06-11 11:06:06 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = apk_solver_solve(db, solver_flags, world, &changeset);
|
2013-06-12 14:00:43 +00:00
|
|
|
if (r == 0)
|
2013-06-11 11:06:06 +00:00
|
|
|
r = apk_solver_commit_changeset(db, &changeset, world);
|
2013-06-12 14:00:43 +00:00
|
|
|
else
|
2013-06-11 11:06:06 +00:00
|
|
|
apk_solver_print_errors(db, &changeset, world);
|
|
|
|
|
2013-06-12 14:00:43 +00:00
|
|
|
apk_change_array_free(&changeset.changes);
|
2013-06-11 11:06:06 +00:00
|
|
|
return r;
|
|
|
|
}
|