projects/aports·blob v2.6.8 · refs

646 lines · 16.9 KiB

1/* commit.c - Alpine Package Keeper (APK)
2 * Apply solver calculated changes to database.
3 *
4 * Copyright (C) 2008-2013 Timo Teräs <[email protected]>
5 * All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation. See http://www.gnu.org/ for details.
10 */
11
12#include <stdint.h>
13#include <unistd.h>
14#include "apk_defines.h"
15#include "apk_database.h"
16#include "apk_package.h"
17#include "apk_solver.h"
18
19#include "apk_print.h"
20
21static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
22{
23 if (pkg->repos & db->available_repos)
24 return TRUE;
25 return FALSE;
26}
27
28static int print_change(struct apk_database *db, struct apk_change *change,
29 int cur, int total)
30{
31 struct apk_name *name;
32 struct apk_package *oldpkg = change->old_pkg;
33 struct apk_package *newpkg = change->new_pkg;
34 const char *msg = NULL;
35 char status[32];
36 apk_blob_t *oneversion = NULL;
37 int r;
38
39 snprintf(status, sizeof(status), "(%i/%i)", cur+1, total);
40
41 name = newpkg ? newpkg->name : oldpkg->name;
42 if (oldpkg == NULL) {
43 msg = "Installing";
44 oneversion = newpkg->version;
45 } else if (newpkg == NULL) {
46 msg = "Purging";
47 oneversion = oldpkg->version;
48 } else if (newpkg == oldpkg) {
49 if (change->reinstall) {
50 if (pkg_available(db, newpkg))
51 msg = "Reinstalling";
52 else
53 msg = "[APK unavailable, skipped] Reinstalling";
54 } else if (change->old_repository_tag != change->new_repository_tag) {
55 msg = "Updating pinning";
56 }
57 oneversion = newpkg->version;
58 } else {
59 r = apk_pkg_version_compare(newpkg, oldpkg);
60 switch (r) {
61 case APK_VERSION_LESS:
62 msg = "Downgrading";
63 break;
64 case APK_VERSION_EQUAL:
65 msg = "Replacing";
66 break;
67 case APK_VERSION_GREATER:
68 msg = "Upgrading";
69 break;
70 }
71 }
72 if (msg == NULL)
73 return FALSE;
74
75 if (oneversion) {
76 apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT ")",
77 status, msg,
78 name->name,
79 BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
80 BLOB_PRINTF(*oneversion));
81 } else {
82 apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT " -> " BLOB_FMT ")",
83 status, msg,
84 name->name,
85 BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
86 BLOB_PRINTF(*oldpkg->version),
87 BLOB_PRINTF(*newpkg->version));
88 }
89 return TRUE;
90}
91
92struct apk_stats {
93 unsigned int changes;
94 size_t bytes;
95 unsigned int packages;
96};
97
98static void count_change(struct apk_change *change, struct apk_stats *stats)
99{
100 if (change->new_pkg != change->old_pkg || change->reinstall) {
101 if (change->new_pkg != NULL) {
102 stats->bytes += change->new_pkg->installed_size;
103 stats->packages++;
104 }
105 if (change->old_pkg != NULL)
106 stats->packages++;
107 stats->changes++;
108 } else if (change->new_repository_tag != change->old_repository_tag) {
109 stats->packages++;
110 stats->changes++;
111 }
112}
113
114struct progress {
115 struct apk_stats done;
116 struct apk_stats total;
117 struct apk_package *pkg;
118};
119
120static void progress_cb(void *ctx, size_t installed_bytes)
121{
122 struct progress *prog = (struct progress *) ctx;
123 apk_print_progress(prog->done.bytes + prog->done.packages + installed_bytes,
124 prog->total.bytes + prog->total.packages);
125}
126
127static int dump_packages(struct apk_changeset *changeset,
128 int (*cmp)(struct apk_change *change),
129 const char *msg)
130{
131 struct apk_change *change;
132 struct apk_name *name;
133 struct apk_indent indent = { .indent = 2 };
134 int match = 0;
135
136 foreach_array_item(change, changeset->changes) {
137 if (!cmp(change))
138 continue;
139 if (match == 0)
140 printf("%s:\n", msg);
141 if (change->new_pkg != NULL)
142 name = change->new_pkg->name;
143 else
144 name = change->old_pkg->name;
145
146 apk_print_indented(&indent, APK_BLOB_STR(name->name));
147 match++;
148 }
149 if (match)
150 printf("\n");
151 return match;
152}
153
154static int cmp_remove(struct apk_change *change)
155{
156 return change->new_pkg == NULL;
157}
158
159static int cmp_new(struct apk_change *change)
160{
161 return change->old_pkg == NULL;
162}
163
164static int cmp_reinstall(struct apk_change *change)
165{
166 return change->reinstall;
167}
168
169static int cmp_downgrade(struct apk_change *change)
170{
171 if (change->new_pkg == NULL || change->old_pkg == NULL)
172 return 0;
173 if (apk_pkg_version_compare(change->new_pkg, change->old_pkg)
174 & APK_VERSION_LESS)
175 return 1;
176 return 0;
177}
178
179static int cmp_upgrade(struct apk_change *change)
180{
181 if (change->new_pkg == NULL || change->old_pkg == NULL)
182 return 0;
183
184 /* Count swapping package as upgrade too - this can happen if
185 * same package version is used after it was rebuilt against
186 * newer libraries. Basically, different (and probably newer)
187 * package, but equal version number. */
188 if ((apk_pkg_version_compare(change->new_pkg, change->old_pkg) &
189 (APK_VERSION_GREATER | APK_VERSION_EQUAL)) &&
190 (change->new_pkg != change->old_pkg))
191 return 1;
192
193 return 0;
194}
195
196static void run_triggers(struct apk_database *db, struct apk_changeset *changeset)
197{
198 struct apk_change *change;
199 struct apk_installed_package *ipkg;
200
201 if (apk_db_fire_triggers(db) == 0)
202 return;
203
204 foreach_array_item(change, changeset->changes) {
205 struct apk_package *pkg = change->new_pkg;
206 if (pkg == NULL)
207 continue;
208 ipkg = pkg->ipkg;
209 if (ipkg == NULL || ipkg->pending_triggers->num == 0)
210 continue;
211
212 *apk_string_array_add(&ipkg->pending_triggers) = NULL;
213 apk_ipkg_run_script(ipkg, db, APK_SCRIPT_TRIGGER,
214 ipkg->pending_triggers->item);
215 apk_string_array_free(&ipkg->pending_triggers);
216 }
217}
218
219int apk_solver_commit_changeset(struct apk_database *db,
220 struct apk_changeset *changeset,
221 struct apk_dependency_array *world)
222{
223 struct progress prog;
224 struct apk_change *change;
225 char buf[32], size_unit;
226 ssize_t size_diff = 0;
227 int r, errors = 0;
228
229 if (apk_db_check_world(db, world) != 0) {
230 apk_error("Not committing changes due to missing repository tags. Use --force to override.");
231 return -1;
232 }
233
234 if (changeset->changes == NULL)
235 goto all_done;
236
237 /* Count what needs to be done */
238 memset(&prog, 0, sizeof(prog));
239 foreach_array_item(change, changeset->changes) {
240 count_change(change, &prog.total);
241 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 size_unit = 'K';
247 if (abs(size_diff) > 10000) {
248 size_diff /= 1024;
249 size_unit = 'M';
250 }
251
252 if ((apk_verbosity > 1 || (apk_flags & APK_INTERACTIVE)) &&
253 !(apk_flags & APK_SIMULATE)) {
254 r = dump_packages(changeset, cmp_remove,
255 "The following packages will be REMOVED");
256 r += dump_packages(changeset, cmp_downgrade,
257 "The following packages will be DOWNGRADED");
258 if (r || (apk_flags & APK_INTERACTIVE) || apk_verbosity > 2) {
259 r += dump_packages(changeset, cmp_new,
260 "The following NEW packages will be installed");
261 r += dump_packages(changeset, cmp_upgrade,
262 "The following packages will be upgraded");
263 r += dump_packages(changeset, cmp_reinstall,
264 "The following packages will be reinstalled");
265 printf("After this operation, %zd %ciB of %s.\n",
266 (size_diff < 0) ? -size_diff : size_diff,
267 size_unit,
268 (size_diff < 0) ?
269 "disk space will be freed" :
270 "additional disk space will be used");
271 }
272 if (r > 0 && (apk_flags & APK_INTERACTIVE)) {
273 printf("Do you want to continue [Y/n]? ");
274 fflush(stdout);
275 r = fgetc(stdin);
276 if (r != 'y' && r != 'Y' && r != '\n')
277 return -1;
278 }
279 }
280
281 /* Go through changes */
282 foreach_array_item(change, changeset->changes) {
283 r = change->old_pkg &&
284 (change->old_pkg->ipkg->broken_files ||
285 change->old_pkg->ipkg->broken_script);
286 if (print_change(db, change, prog.done.changes, prog.total.changes)) {
287 prog.pkg = change->new_pkg;
288 progress_cb(&prog, 0);
289
290 if (!(apk_flags & APK_SIMULATE) &&
291 ((change->old_pkg != change->new_pkg) ||
292 (change->reinstall && pkg_available(db, change->new_pkg)))) {
293 r = apk_db_install_pkg(db, change->old_pkg, change->new_pkg,
294 progress_cb, &prog) != 0;
295 }
296 if (r == 0 && change->new_pkg && change->new_pkg->ipkg)
297 change->new_pkg->ipkg->repository_tag = change->new_repository_tag;
298 }
299 errors += r;
300 count_change(change, &prog.done);
301 }
302 apk_print_progress(prog.total.bytes + prog.total.packages,
303 prog.total.bytes + prog.total.packages);
304
305 apk_db_update_directory_permissions(db);
306 run_triggers(db, changeset);
307
308all_done:
309 apk_dependency_array_copy(&db->world, world);
310 apk_db_write_config(db);
311
312 if (!db->performing_self_upgrade) {
313 if (errors)
314 snprintf(buf, sizeof(buf), "%d errors;", errors);
315 else
316 strcpy(buf, "OK:");
317 if (apk_verbosity > 1) {
318 apk_message("%s %d packages, %d dirs, %d files, %zu MiB",
319 buf,
320 db->installed.stats.packages,
321 db->installed.stats.dirs,
322 db->installed.stats.files,
323 db->installed.stats.bytes / (1024 * 1024));
324 } else {
325 apk_message("%s %zu MiB in %d packages",
326 buf,
327 db->installed.stats.bytes / (1024 * 1024),
328 db->installed.stats.packages);
329 }
330 }
331
332 return errors;
333}
334
335enum {
336 STATE_PRESENT = 0x80000000,
337 STATE_MISSING = 0x40000000,
338 STATE_COUNT_MASK = 0x0000ffff,
339};
340
341struct print_state {
342 struct apk_database *db;
343 struct apk_dependency_array *world;
344 struct apk_indent i;
345 struct apk_name_array *missing;
346 const char *label;
347 int num_labels;
348 int match;
349};
350
351static void label_start(struct print_state *ps, const char *text)
352{
353 if (ps->label) {
354 printf(" %s:\n", ps->label);
355 ps->label = NULL;
356 ps->i.x = ps->i.indent = 0;
357 ps->num_labels++;
358 }
359 if (ps->i.x == 0) {
360 ps->i.x = printf(" %s", text);
361 ps->i.indent = ps->i.x + 1;
362 }
363}
364static void label_end(struct print_state *ps)
365{
366 if (ps->i.x != 0) {
367 printf("\n");
368 ps->i.x = ps->i.indent = 0;
369 }
370}
371
372static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
373{
374 struct apk_database *db = ps->db;
375 int i;
376
377 if (pkg->ipkg != NULL)
378 return;
379
380 if (!(pkg->repos & db->available_repos)) {
381 label_start(ps, "masked in:");
382 apk_print_indented_fmt(&ps->i, "--no-network");
383 } else if (pkg->repos == BIT(APK_REPOSITORY_CACHED)) {
384 label_start(ps, "masked in:");
385 apk_print_indented_fmt(&ps->i, "cache");
386 } else {
387 if (pkg->repos & apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK | BIT(tag)))
388 return;
389 for (i = 0; i < db->num_repo_tags; i++) {
390 if (pkg->repos & db->repo_tags[i].allowed_repos) {
391 label_start(ps, "masked in:");
392 apk_print_indented(&ps->i, db->repo_tags[i].tag);
393 }
394 }
395 }
396 label_end(ps);
397}
398
399static void print_conflicts(struct print_state *ps, struct apk_package *pkg)
400{
401 struct apk_provider *p;
402 struct apk_dependency *d;
403 char tmp[256];
404 int once;
405
406 foreach_array_item(p, pkg->name->providers) {
407 if (p->pkg == pkg || !p->pkg->marked)
408 continue;
409 label_start(ps, "conflicts:");
410 apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p->pkg));
411 }
412 foreach_array_item(d, pkg->provides) {
413 once = 1;
414 foreach_array_item(p, d->name->providers) {
415 if (!p->pkg->marked)
416 continue;
417 if (d->version == &apk_null_blob &&
418 p->version == &apk_null_blob)
419 continue;
420 if (once && p->pkg == pkg) {
421 once = 0;
422 continue;
423 }
424 label_start(ps, "conflicts:");
425 apk_print_indented_fmt(
426 &ps->i, PKG_VER_FMT "[%s]",
427 PKG_VER_PRINTF(p->pkg),
428 apk_dep_snprintf(tmp, sizeof(tmp), d));
429 }
430 }
431 label_end(ps);
432}
433
434static void print_dep(struct apk_package *pkg0, struct apk_dependency *d0, struct apk_package *pkg, void *ctx)
435{
436 struct print_state *ps = (struct print_state *) ctx;
437 const char *label = (ps->match & APK_DEP_SATISFIES) ? "satisfies:" : "breaks:";
438 char tmp[256];
439
440 label_start(ps, label);
441 if (pkg0 == NULL)
442 apk_print_indented_fmt(&ps->i, "world[%s]", apk_dep_snprintf(tmp, sizeof(tmp), d0));
443 else
444 apk_print_indented_fmt(&ps->i, PKG_VER_FMT "[%s]",
445 PKG_VER_PRINTF(pkg0),
446 apk_dep_snprintf(tmp, sizeof(tmp), d0));
447}
448
449static void print_deps(struct print_state *ps, struct apk_package *pkg, int match)
450{
451 ps->match = match;
452 match |= APK_FOREACH_MARKED | APK_FOREACH_DEP;
453 apk_pkg_foreach_matching_dependency(NULL, ps->world, match|apk_foreach_genid(), pkg, print_dep, ps);
454 apk_pkg_foreach_reverse_dependency(pkg, match|apk_foreach_genid(), print_dep, ps);
455 label_end(ps);
456}
457
458static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
459{
460 char pkgtext[256];
461
462 snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
463 ps->label = pkgtext;
464
465 print_pinning_errors(ps, pkg, tag);
466 print_conflicts(ps, pkg);
467 print_deps(ps, pkg, APK_DEP_CONFLICTS);
468 if (ps->label == NULL)
469 print_deps(ps, pkg, APK_DEP_SATISFIES);
470}
471
472static void analyze_name(struct print_state *ps, struct apk_name *name)
473{
474 struct apk_name **pname0, *name0;
475 struct apk_provider *p0;
476 struct apk_dependency *d0;
477 char tmp[256];
478 int refs;
479
480 if (name->providers->num) {
481 snprintf(tmp, sizeof(tmp), "%s (virtual)", name->name);
482 ps->label = tmp;
483
484 label_start(ps, "provided by:");
485 foreach_array_item(p0, name->providers)
486 p0->pkg->name->state_int++;
487 foreach_array_item(p0, name->providers) {
488 name0 = p0->pkg->name;
489 refs = (name0->state_int & STATE_COUNT_MASK);
490 if (refs == name0->providers->num) {
491 /* name only */
492 apk_print_indented(&ps->i, APK_BLOB_STR(name0->name));
493 name0->state_int &= ~STATE_COUNT_MASK;
494 } else if (refs > 0) {
495 /* individual package */
496 apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p0->pkg));
497 name0->state_int--;
498 }
499 }
500 label_end(ps);
501 } else {
502 snprintf(tmp, sizeof(tmp), "%s (missing)", name->name);
503 ps->label = tmp;
504 }
505
506 label_start(ps, "required by:");
507 foreach_array_item(d0, ps->world) {
508 if (d0->name != name || d0->conflict)
509 continue;
510 apk_print_indented_fmt(&ps->i, "world[%s]",
511 apk_dep_snprintf(tmp, sizeof(tmp), d0));
512 }
513 foreach_array_item(pname0, name->rdepends) {
514 name0 = *pname0;
515 foreach_array_item(p0, name0->providers) {
516 if (!p0->pkg->marked)
517 continue;
518 foreach_array_item(d0, p0->pkg->depends) {
519 if (d0->name != name || d0->conflict)
520 continue;
521 apk_print_indented_fmt(&ps->i,
522 PKG_VER_FMT "[%s]",
523 PKG_VER_PRINTF(p0->pkg),
524 apk_dep_snprintf(tmp, sizeof(tmp), d0));
525 break;
526 }
527 if (d0 != NULL)
528 break;
529 }
530 }
531 label_end(ps);
532}
533
534static void analyze_deps(struct print_state *ps, struct apk_dependency_array *deps)
535{
536 struct apk_dependency *d0;
537 struct apk_name *name0;
538
539 foreach_array_item(d0, deps) {
540 name0 = d0->name;
541 if (d0->conflict)
542 continue;
543 if ((name0->state_int & (STATE_PRESENT | STATE_MISSING)) != 0)
544 continue;
545 name0->state_int |= STATE_MISSING;
546 analyze_name(ps, name0);
547 }
548}
549
550void apk_solver_print_errors(struct apk_database *db,
551 struct apk_changeset *changeset,
552 struct apk_dependency_array *world)
553{
554 struct print_state ps;
555 struct apk_change *change;
556 struct apk_dependency *p;
557
558 /* ERROR: unsatisfiable dependencies:
559 * name:
560 * required by: a b c d e
561 * not available in any repository
562 * name (virtual):
563 * required by: a b c d e
564 * provided by: foo bar zed
565 * pkg-1.2:
566 * masked by: @testing
567 * satisfies: a[pkg]
568 * conflicts: pkg-2.0 foo-1.2 bar-1.2
569 * breaks: b[pkg>2] c[foo>2] d[!pkg]
570 *
571 * When two packages provide same name 'foo':
572 * a-1:
573 * satisfies: world[a]
574 * conflicts: b-1[foo]
575 * b-1:
576 * satisfies: world[b]
577 * conflicts: a-1[foo]
578 *
579 * c-1:
580 * satisfies: world[a]
581 * conflicts: c-1[foo] (self-conflict by providing foo twice)
582 *
583 * When two packages get pulled in:
584 * a-1:
585 * satisfies: app1[so:a.so.1]
586 * conflicts: a-2
587 * a-2:
588 * satisfies: app2[so:a.so.2]
589 * conflicts: a-1
590 *
591 * satisfies lists all dependencies that is not satisfiable by
592 * any other selected version. or all of them with -v.
593 */
594
595 apk_error("unsatisfiable constraints:");
596
597 /* Construct information about names */
598 foreach_array_item(change, changeset->changes) {
599 struct apk_package *pkg = change->new_pkg;
600 if (pkg == NULL)
601 continue;
602 pkg->marked = 1;
603 pkg->name->state_int |= STATE_PRESENT;
604 foreach_array_item(p, pkg->provides)
605 p->name->state_int |= STATE_PRESENT;
606 }
607
608 /* Analyze is package, and missing names referred to */
609 ps = (struct print_state) {
610 .db = db,
611 .world = world,
612 };
613 analyze_deps(&ps, world);
614 foreach_array_item(change, changeset->changes) {
615 struct apk_package *pkg = change->new_pkg;
616 if (pkg == NULL)
617 continue;
618 analyze_package(&ps, pkg, change->new_repository_tag);
619 analyze_deps(&ps, pkg->depends);
620 }
621
622 if (ps.num_labels == 0)
623 printf(" Huh? Error reporter did not find the broken constraints.\n");
624}
625
626int apk_solver_commit(struct apk_database *db,
627 unsigned short solver_flags,
628 struct apk_dependency_array *world)
629{
630 struct apk_changeset changeset = {};
631 int r;
632
633 if (apk_db_check_world(db, world) != 0) {
634 apk_error("Not committing changes due to missing repository tags. Use --force to override.");
635 return -1;
636 }
637
638 r = apk_solver_solve(db, solver_flags, world, &changeset);
639 if (r == 0)
640 r = apk_solver_commit_changeset(db, &changeset, world);
641 else
642 apk_solver_print_errors(db, &changeset, world);
643
644 apk_change_array_free(&changeset.changes);
645 return r;
646}