commit.c 16.7 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
	if (!(pkg->repos & db->available_repos)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "--no-network");
382 383 384
	} else if (pkg->repos == BIT(APK_REPOSITORY_CACHED)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "cache");
385 386 387 388 389 390 391 392
	} 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);
			}
393
		}
394
	}
395
	label_end(ps);
396 397
}

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

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

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

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

449
static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
450
{
451
	char pkgtext[256];
452

453 454
	snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
	ps->label = pkgtext;
455

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

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];
469
	int refs;
470 471 472 473 474 475

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

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

497 498 499
	label_start(ps, "required by:");
	foreach_array_item(d0, ps->world) {
		if (d0->name != name || d0->conflict)
500
			continue;
501 502 503 504 505 506
		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) {
507
			if (!p0->pkg->marked)
508 509 510 511 512 513 514 515 516 517 518 519
				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;
520
		}
521 522 523
	}
	label_end(ps);
}
524

525 526 527
static void analyze_deps(struct print_state *ps, struct apk_dependency_array *deps)
{
	struct apk_dependency *d0;
528
	struct apk_name *name0;
529 530

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

void apk_solver_print_errors(struct apk_database *db,
			     struct apk_changeset *changeset,
			     struct apk_dependency_array *world)
{
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 581 582 583
	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.
584
	 */
585 586
 
	apk_error("unsatisfiable constraints:");
587

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

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

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

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);
630
	if (r == 0)
631
		r = apk_solver_commit_changeset(db, &changeset, world);
632
	else
633 634
		apk_solver_print_errors(db, &changeset, world);

635
	apk_change_array_free(&changeset.changes);
636 637
	return r;
}