commit.c 16.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/* 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.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#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;
35
	char status[32];
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
	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))
51
				msg = "Reinstalling";
52
			else
53
				msg = "[APK unavailable, skipped] Reinstalling";
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
		} 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) {
76 77 78 79
		apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT ")",
			    status, msg,
			    name->name,
			    BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
80 81
			    BLOB_PRINTF(*oneversion));
	} else {
82 83 84 85
		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),
86 87 88 89 90 91 92 93
			    BLOB_PRINTF(*oldpkg->version),
			    BLOB_PRINTF(*newpkg->version));
	}
	return TRUE;
}

struct apk_stats {
	unsigned int changes;
94
	size_t bytes;
95 96 97 98 99
	unsigned int packages;
};

static void count_change(struct apk_change *change, struct apk_stats *stats)
{
100
	if (change->new_pkg != change->old_pkg || change->reinstall) {
101 102
		if (change->new_pkg != NULL) {
			stats->bytes += change->new_pkg->installed_size;
103
			stats->packages++;
104 105
		}
		if (change->old_pkg != NULL)
106
			stats->packages++;
107
		stats->changes++;
108 109
	} else if (change->new_repository_tag != change->old_repository_tag) {
		stats->packages++;
110 111 112 113 114 115 116 117 118 119
		stats->changes++;
	}
}

struct progress {
	struct apk_stats done;
	struct apk_stats total;
	struct apk_package *pkg;
};

120
static void progress_cb(void *ctx, size_t installed_bytes)
121 122
{
	struct progress *prog = (struct progress *) ctx;
123 124
	apk_print_progress(prog->done.bytes + prog->done.packages + installed_bytes,
			   prog->total.bytes + prog->total.packages);
125 126 127 128 129 130 131 132 133
}

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 };
134
	int match = 0;
135

136
	foreach_array_item(change, changeset->changes) {
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
		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;
}

164 165 166 167 168
static int cmp_reinstall(struct apk_change *change)
{
	return change->reinstall;
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
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)
{
198 199
	struct apk_change *change;
	struct apk_installed_package *ipkg;
200 201 202 203

	if (apk_db_fire_triggers(db) == 0)
		return;

204 205
	foreach_array_item(change, changeset->changes) {
		struct apk_package *pkg = change->new_pkg;
206 207 208
		if (pkg == NULL)
			continue;
		ipkg = pkg->ipkg;
Natanael Copa's avatar
Natanael Copa committed
209
		if (ipkg == NULL || ipkg->pending_triggers->num == 0)
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
			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);
	}
}

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;
225 226 227
	char buf[32], size_unit;
	ssize_t size_diff = 0;
	int r, errors = 0;
228 229 230 231 232 233 234 235 236 237 238

	if (apk_db_check_world(db, world) != 0) {
		apk_error("Not committing changes due to missing repository tags. Use --force to override.");
		return -1;
	}

	if (changeset->changes == NULL)
		goto all_done;

	/* Count what needs to be done */
	memset(&prog, 0, sizeof(prog));
239
	foreach_array_item(change, changeset->changes) {
240 241
		count_change(change, &prog.total);
		if (change->new_pkg)
242
			size_diff += change->new_pkg->installed_size / 1024;
243
		if (change->old_pkg)
244
			size_diff -= change->old_pkg->installed_size / 1024;
245 246 247 248 249 250 251
	}
	size_unit = 'K';
	if (abs(size_diff) > 10000) {
		size_diff /= 1024;
		size_unit = 'M';
	}

252 253
	if ((apk_verbosity > 1 || (apk_flags & APK_INTERACTIVE)) &&
	    !(apk_flags & APK_SIMULATE)) {
254 255 256 257 258
		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) {
259 260 261 262 263 264
			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");
Timo Teräs's avatar
Timo Teräs committed
265 266 267
			printf("After this operation, %zd %ciB of %s.\n",
				(size_diff < 0) ? -size_diff : size_diff,
				size_unit,
268
				(size_diff < 0) ?
Timo Teräs's avatar
Timo Teräs committed
269 270
				"disk space will be freed" :
				"additional disk space will be used");
271
		}
272
		if (r > 0 && (apk_flags & APK_INTERACTIVE)) {
273 274 275 276 277 278 279 280 281
			printf("Do you want to continue [Y/n]? ");
			fflush(stdout);
			r = fgetc(stdin);
			if (r != 'y' && r != 'Y' && r != '\n')
				return -1;
		}
	}

	/* Go through changes */
282
	foreach_array_item(change, changeset->changes) {
283 284 285
		r = change->old_pkg &&
			(change->old_pkg->ipkg->broken_files ||
			 change->old_pkg->ipkg->broken_script);
286 287 288 289
		if (print_change(db, change, prog.done.changes, prog.total.changes)) {
			prog.pkg = change->new_pkg;
			progress_cb(&prog, 0);

290 291 292 293 294
			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;
295
			}
296 297
			if (r == 0 && change->new_pkg && change->new_pkg->ipkg)
				change->new_pkg->ipkg->repository_tag = change->new_repository_tag;
298
		}
299
		errors += r;
300 301
		count_change(change, &prog.done);
	}
302 303
	apk_print_progress(prog.total.bytes + prog.total.packages,
			   prog.total.bytes + prog.total.packages);
304 305 306 307 308 309 310

	run_triggers(db, changeset);

all_done:
	apk_dependency_array_copy(&db->world, world);
	apk_db_write_config(db);

311 312 313 314 315
	if (!db->performing_self_update) {
		if (errors)
			snprintf(buf, sizeof(buf), "%d errors;", errors);
		else
			strcpy(buf, "OK:");
316
		if (apk_verbosity > 1) {
317 318
			apk_message("%s %d packages, %d dirs, %d files, %zu MiB",
				    buf,
319 320 321 322 323
				    db->installed.stats.packages,
				    db->installed.stats.dirs,
				    db->installed.stats.files,
				    db->installed.stats.bytes / (1024 * 1024));
		} else {
324 325
			apk_message("%s %zu MiB in %d packages",
				    buf,
326 327 328 329 330
				    db->installed.stats.bytes / (1024 * 1024),
				    db->installed.stats.packages);
		}
	}

331
	return 0;
332 333
}

334
enum {
335 336 337
	STATE_PRESENT		= 0x80000000,
	STATE_MISSING		= 0x40000000,
	STATE_COUNT_MASK	= 0x0000ffff,
338
};
339

340 341 342 343 344 345 346 347 348
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;
};
349

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
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;
	}
369 370
}

371
static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
372
{
373
	struct apk_database *db = ps->db;
374 375 376 377 378
	int i;

	if (pkg->ipkg != NULL)
		return;

379 380 381 382 383 384 385 386 387 388 389
	if (!(pkg->repos & db->available_repos)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "--no-network");
	} 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);
			}
390
		}
391
	}
392
	label_end(ps);
393 394
}

395
static void print_conflicts(struct print_state *ps, struct apk_package *pkg)
396
{
397 398
	struct apk_provider *p;
	struct apk_dependency *d;
399
	char tmp[256];
400

401
	foreach_array_item(p, pkg->name->providers) {
402
		if (p->pkg == pkg || !p->pkg->marked)
403
			continue;
404 405 406 407 408
		label_start(ps, "conflicts:");
		apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p->pkg));
	}
	foreach_array_item(d, pkg->provides) {
		foreach_array_item(p, d->name->providers) {
409 410 411
			if (!p->pkg->marked)
				continue;
			if (p->pkg == pkg && p->version == d->version)
412 413 414
				continue;
			label_start(ps, "conflicts:");
			apk_print_indented_fmt(
415
				&ps->i, PKG_VER_FMT "[%s]",
416
				PKG_VER_PRINTF(p->pkg),
417
				apk_dep_snprintf(tmp, sizeof(tmp), d));
418 419 420 421
		}
	}
	label_end(ps);
}
422

423
static void print_dep(struct apk_package *pkg0, struct apk_dependency *d0, struct apk_package *pkg, void *ctx)
424 425
{
	struct print_state *ps = (struct print_state *) ctx;
426
	const char *label = (ps->match & APK_DEP_SATISFIES) ? "satisfies:" : "breaks:";
427
	char tmp[256];
428

429 430 431 432 433 434 435 436 437 438 439 440
	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;
441 442
	apk_pkg_foreach_matching_dependency(NULL, ps->world, match, pkg, print_dep, ps);
	apk_pkg_foreach_reverse_dependency(pkg, match, print_dep, ps);
443
	label_end(ps);
444 445
}

446
static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
447
{
448
	char pkgtext[256];
449

450 451
	snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
	ps->label = pkgtext;
452

453 454
	print_pinning_errors(ps, pkg, tag);
	print_conflicts(ps, pkg);
455
	print_deps(ps, pkg, APK_DEP_CONFLICTS | APK_FOREACH_MARKED);
456
	if (ps->label == NULL)
457
		print_deps(ps, pkg, APK_DEP_SATISFIES | APK_FOREACH_MARKED);
458 459 460 461 462 463 464 465
}

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];
466
	int refs;
467 468 469 470 471 472

	if (name->providers->num) {
		snprintf(tmp, sizeof(tmp), "%s (virtual)", name->name);
		ps->label = tmp;

		label_start(ps, "provided by:");
473 474
		foreach_array_item(p0, name->providers)
			p0->pkg->name->state_int++;
475
		foreach_array_item(p0, name->providers) {
476 477 478 479 480 481 482 483 484 485 486
			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--;
			}
487 488 489 490 491 492
		}
		label_end(ps);
	} else {
		snprintf(tmp, sizeof(tmp), "%s (missing)", name->name);
		ps->label = tmp;
	}
493

494 495 496
	label_start(ps, "required by:");
	foreach_array_item(d0, ps->world) {
		if (d0->name != name || d0->conflict)
497
			continue;
498 499 500 501 502 503
		apk_print_indented_fmt(&ps->i, "world[%s]",
			apk_dep_snprintf(tmp, sizeof(tmp), d0));
	}
	foreach_array_item(pname0, name->rdepends) {
		name0 = *pname0;
		foreach_array_item(p0, name0->providers) {
504
			if (!p0->pkg->marked)
505 506 507 508 509 510 511 512 513 514 515 516
				continue;
			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;
517
		}
518 519 520
	}
	label_end(ps);
}
521

522 523 524
static void analyze_deps(struct print_state *ps, struct apk_dependency_array *deps)
{
	struct apk_dependency *d0;
525
	struct apk_name *name0;
526 527

	foreach_array_item(d0, deps) {
528
		name0 = d0->name;
529 530
		if (d0->conflict)
			continue;
531
		if ((name0->state_int & (STATE_PRESENT | STATE_MISSING)) != 0)
532
			continue;
533 534
		name0->state_int |= STATE_MISSING;
		analyze_name(ps, name0);
535 536 537 538 539 540 541
	}
}

void apk_solver_print_errors(struct apk_database *db,
			     struct apk_changeset *changeset,
			     struct apk_dependency_array *world)
{
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
	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.
581
	 */
582 583
 
	apk_error("unsatisfiable constraints:");
584

585 586 587
	/* Construct information about names */
	foreach_array_item(change, changeset->changes) {
		struct apk_package *pkg = change->new_pkg;
588 589
		if (pkg == NULL)
			continue;
590
		pkg->marked = 1;
591
		pkg->name->state_int |= STATE_PRESENT;
592
		foreach_array_item(p, pkg->provides)
593
			p->name->state_int |= STATE_PRESENT;
594 595
	}

596 597 598 599 600 601 602
	/* 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) {
603 604 605
		struct apk_package *pkg = change->new_pkg;
		if (pkg == NULL)
			continue;
606 607
		analyze_package(&ps, pkg, change->new_repository_tag);
		analyze_deps(&ps, pkg->depends);
608 609
	}

610 611
	if (ps.num_labels == 0)
		printf("  Huh? Error reporter did not find the broken constraints.\n");
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
}

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) {
		apk_error("Not committing changes due to missing repository tags. Use --force to override.");
		return -1;
	}

	r = apk_solver_solve(db, solver_flags, world, &changeset);
627
	if (r == 0)
628
		r = apk_solver_commit_changeset(db, &changeset, world);
629
	else
630 631
		apk_solver_print_errors(db, &changeset, world);

632
	apk_change_array_free(&changeset.changes);
633 634
	return r;
}