]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/transaction.c
util: Add tests for pakfire_format_size
[people/stevee/pakfire.git] / src / libpakfire / transaction.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2013 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <errno.h>
22 #include <stdlib.h>
23
24 #include <openssl/crypto.h>
25 #include <openssl/evp.h>
26 #include <solv/transaction.h>
27
28 #include <pakfire/archive.h>
29 #include <pakfire/db.h>
30 #include <pakfire/downloader.h>
31 #include <pakfire/execute.h>
32 #include <pakfire/file.h>
33 #include <pakfire/filelist.h>
34 #include <pakfire/i18n.h>
35 #include <pakfire/logging.h>
36 #include <pakfire/package.h>
37 #include <pakfire/pakfire.h>
38 #include <pakfire/private.h>
39 #include <pakfire/repo.h>
40 #include <pakfire/transaction.h>
41 #include <pakfire/ui.h>
42 #include <pakfire/util.h>
43
44 struct pakfire_transaction {
45 struct pakfire* pakfire;
46 int nrefs;
47
48 Transaction* transaction;
49 char** userinstalled;
50
51 struct pakfire_archive** archives;
52 struct pakfire_package** packages;
53 size_t num;
54 };
55
56 enum pakfire_actions {
57 PAKFIRE_ACTION_NOOP = 0,
58 PAKFIRE_ACTION_VERIFY,
59 PAKFIRE_ACTION_EXECUTE,
60 PAKFIRE_ACTION_PRETRANS,
61 PAKFIRE_ACTION_POSTTRANS,
62 };
63
64 enum pakfire_steps {
65 PAKFIRE_STEP_IGNORE = 0,
66 PAKFIRE_STEP_INSTALL,
67 PAKFIRE_STEP_REINSTALL,
68 PAKFIRE_STEP_ERASE,
69 PAKFIRE_STEP_UPGRADE,
70 PAKFIRE_STEP_DOWNGRADE,
71 PAKFIRE_STEP_OBSOLETE,
72 };
73
74 static enum pakfire_steps pakfire_transaction_get_step_type(
75 struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
76 int type = transaction_type(transaction->transaction, pakfire_package_id(pkg),
77 SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_CHANGE_IS_REINSTALL);
78
79 // Translate solver types into our own types
80 switch (type) {
81 case SOLVER_TRANSACTION_INSTALL:
82 case SOLVER_TRANSACTION_MULTIINSTALL:
83 return PAKFIRE_STEP_INSTALL;
84
85 case SOLVER_TRANSACTION_REINSTALL:
86 case SOLVER_TRANSACTION_MULTIREINSTALL:
87 return PAKFIRE_STEP_REINSTALL;
88
89 case SOLVER_TRANSACTION_ERASE:
90 return PAKFIRE_STEP_ERASE;
91
92 case SOLVER_TRANSACTION_DOWNGRADE:
93 return PAKFIRE_STEP_DOWNGRADE;
94
95 case SOLVER_TRANSACTION_UPGRADE:
96 return PAKFIRE_STEP_UPGRADE;
97
98 case SOLVER_TRANSACTION_OBSOLETES:
99 return PAKFIRE_STEP_OBSOLETE;
100
101 // Anything we don't care about
102 case SOLVER_TRANSACTION_IGNORE:
103 case SOLVER_TRANSACTION_REINSTALLED:
104 case SOLVER_TRANSACTION_DOWNGRADED:
105 default:
106 return PAKFIRE_STEP_IGNORE;
107 }
108 }
109
110 static void pakfire_transaction_free_archives_and_packages(
111 struct pakfire_transaction* transaction) {
112 if (transaction->archives) {
113 for (unsigned int i = 0; i < transaction->num; i++)
114 if (transaction->archives[i])
115 pakfire_archive_unref(transaction->archives[i]);
116 free(transaction->archives);
117
118 transaction->archives = NULL;
119 }
120
121 if (transaction->packages) {
122 for (unsigned int i = 0; i < transaction->num; i++)
123 if (transaction->packages[i])
124 pakfire_package_unref(transaction->packages[i]);
125 free(transaction->packages);
126
127 transaction->packages = NULL;
128 }
129 }
130
131 static void pakfire_transaction_free(struct pakfire_transaction* transaction) {
132 pakfire_transaction_free_archives_and_packages(transaction);
133
134 if (transaction->userinstalled) {
135 for (char** userinstalled = transaction->userinstalled; *userinstalled; userinstalled++)
136 free(*userinstalled);
137 free(transaction->userinstalled);
138 }
139 transaction_free(transaction->transaction);
140
141 pakfire_unref(transaction->pakfire);
142 free(transaction);
143 }
144
145 static int pakfire_transaction_import_transaction(
146 struct pakfire_transaction* transaction, Solver* solver) {
147 // Clone the transaction to keep a copy of it
148 Transaction* t = solver_create_transaction(solver);
149 if (!t)
150 return 1;
151
152 transaction->transaction = t;
153
154 // Order the transaction
155 transaction_order(t, 0);
156
157 // Free any previous content
158 pakfire_transaction_free_archives_and_packages(transaction);
159
160 // How many steps?
161 transaction->num = t->steps.count;
162
163 // Allocate space for packages
164 transaction->packages = calloc(transaction->num, sizeof(*transaction->packages));
165 if (!transaction->packages)
166 return 1;
167
168 // Allocate space for archives
169 transaction->archives = calloc(transaction->num, sizeof(*transaction->archives));
170 if (!transaction->archives)
171 return 1;
172
173 // Create all packages
174 for (unsigned int i = 0; i < transaction->num; i++) {
175 transaction->packages[i] = pakfire_package_create_from_solvable(
176 transaction->pakfire, t->steps.elements[i]);
177 }
178
179 return 0;
180 }
181
182 static int pakfire_transaction_import_userinstalled(
183 struct pakfire_transaction* t, Solver* solver) {
184 if (t->userinstalled) {
185 free(t->userinstalled);
186 t->userinstalled = NULL;
187 }
188
189 Queue userinstalled;
190 queue_init(&userinstalled);
191
192 // Fetch a list of all packages that are installed by the user
193 solver_get_userinstalled(solver, &userinstalled, GET_USERINSTALLED_NAMES);
194
195 // Skip everything if the queue is empty
196 if (!userinstalled.count)
197 return 0;
198
199 t->userinstalled = calloc(userinstalled.count + 1, sizeof(*t->userinstalled));
200 if (!t->userinstalled) {
201 ERROR(t->pakfire, "Could not allocate userinstalled\n");
202 return 1;
203 }
204
205 Pool* pool = pakfire_get_solv_pool(t->pakfire);
206
207 // Store the names of all userinstalled packages
208 for (int i = 0; i < userinstalled.count; i++) {
209 const char* package = pool_id2str(pool, userinstalled.elements[i]);
210 t->userinstalled[i] = strdup(package);
211 }
212
213 return 0;
214 }
215
216 int pakfire_transaction_create(struct pakfire_transaction** transaction,
217 struct pakfire* pakfire, Solver* solver) {
218 struct pakfire_transaction* t = calloc(1, sizeof(*t));
219 if (!t)
220 return ENOMEM;
221
222 // Store reference to Pakfire
223 t->pakfire = pakfire_ref(pakfire);
224
225 // Initialize the reference counter
226 t->nrefs = 1;
227
228 // Import transaction
229 int r = pakfire_transaction_import_transaction(t, solver);
230 if (r)
231 goto ERROR;
232
233 // Import userinstalled
234 r = pakfire_transaction_import_userinstalled(t, solver);
235 if (r)
236 goto ERROR;
237
238 *transaction = t;
239 return 0;
240
241 ERROR:
242 pakfire_transaction_free(t);
243 return 1;
244 }
245
246 PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_ref(
247 struct pakfire_transaction* transaction) {
248 transaction->nrefs++;
249
250 return transaction;
251 }
252
253 PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_unref(
254 struct pakfire_transaction* transaction) {
255 if (--transaction->nrefs > 0)
256 return transaction;
257
258 pakfire_transaction_free(transaction);
259 return NULL;
260 }
261
262 PAKFIRE_EXPORT size_t pakfire_transaction_count(struct pakfire_transaction* transaction) {
263 return transaction->num;
264 }
265
266 static ssize_t pakfire_transaction_installsizechange(struct pakfire_transaction* transaction) {
267 ssize_t sizechange = transaction_calc_installsizechange(transaction->transaction);
268
269 // Convert from kbytes to bytes
270 return sizechange * 1024;
271 }
272
273 PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
274 ssize_t size = 0;
275
276 for (unsigned int i = 0; i < transaction->num; i++)
277 size += pakfire_package_get_downloadsize(transaction->packages[i]);
278
279 return size;
280 }
281
282 static void pakfire_transaction_append_line(char*** lines, const char* format, ...) {
283 if (!lines)
284 return;
285
286 char* buffer = NULL;
287 va_list args;
288 int r;
289
290 va_start(args, format);
291 r = vasprintf(&buffer, format, args);
292 va_end(args);
293
294 if (r < 0)
295 return;
296
297 // Count lines
298 unsigned int count = 0;
299 if (*lines) {
300 for (char** l = *lines; *l; l++)
301 count++;
302 }
303
304 // Increase size of array
305 *lines = reallocarray(*lines, count + 2, sizeof(**lines));
306 if (!*lines)
307 return;
308
309 // Append line and terminate lines
310 (*lines)[count] = buffer;
311 (*lines)[count + 1] = NULL;
312 }
313
314 static void pakfire_transaction_add_headline(char*** lines, size_t width, const char* headline) {
315 pakfire_transaction_append_line(lines, "%s\n", headline);
316 }
317
318 static void pakfire_transaction_add_newline(char*** lines, size_t width) {
319 pakfire_transaction_append_line(lines, "\n");
320 }
321
322 static void pakfire_transaction_add_line(char*** lines, size_t width, const char* name,
323 const char* arch, const char* version, const char* repo, const char* size) {
324 pakfire_transaction_append_line(lines, " %-21s %-8s %-21s %-18s %6s \n",
325 name, arch, version, repo, size);
326 }
327
328 static void pakfire_transaction_add_package(char*** lines, size_t width, struct pakfire_package* pkg) {
329 char size[128];
330
331 struct pakfire_repo* repo = pakfire_package_get_repo(pkg);
332
333 // Format size
334 int r = pakfire_format_size(size, pakfire_package_get_size(pkg));
335 if (r < 0)
336 return;
337
338 pakfire_transaction_add_line(lines, width,
339 pakfire_package_get_name(pkg),
340 pakfire_package_get_arch(pkg),
341 pakfire_package_get_evr(pkg),
342 pakfire_repo_get_name(repo),
343 size
344 );
345
346 pakfire_repo_unref(repo);
347 }
348
349 static void pakfire_transaction_add_package_change(char*** lines, size_t width,
350 struct pakfire_package* old_pkg, struct pakfire_package* new_pkg) {
351 // Print the new package first
352 pakfire_transaction_add_package(lines, width, new_pkg);
353
354 pakfire_transaction_append_line(lines,
355 " --> %s\n", pakfire_package_get_nevra(old_pkg));
356 }
357
358 static void pakfire_transaction_add_separator(char*** lines, size_t width) {
359 char* separator = alloca(width + 1);
360
361 for (unsigned int i = 0; i < width; i++)
362 separator[i] = '=';
363 separator[width] = '\0';
364
365 pakfire_transaction_append_line(lines, "%s\n", separator);
366 }
367
368 static void pakfire_transaction_add_usage_line(char*** lines, size_t width,
369 const char* headline, ssize_t size) {
370 char buffer[128];
371
372 int r = pakfire_format_size(buffer, size);
373 if (r < 0)
374 return;
375
376 pakfire_transaction_append_line(lines, "%-21s: %s\n", headline, buffer);
377 }
378
379 static char* pakfire_transaction_join_lines(char** lines) {
380 if (!lines)
381 return NULL;
382
383 size_t size = 0;
384
385 // Determine total length
386 for (char** line = lines; *line; line++)
387 size += strlen(*line);
388
389 // Allocate memory large enough to hold the result
390 char* s = calloc(1, size + 1);
391 if (!s)
392 return s;
393
394 char* p = s;
395
396 for (char** line = lines; *line; line++) {
397 p += snprintf(p, s - p - 1, "%s", *line);
398 }
399
400 return s;
401 }
402
403 PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transaction, size_t width) {
404 char headline[1024];
405
406 Pool* pool = transaction->transaction->pool;
407 const int mode =
408 SOLVER_TRANSACTION_SHOW_OBSOLETES |
409 SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
410
411 char** lines = NULL;
412
413 // Header
414 pakfire_transaction_add_separator(&lines, width);
415 pakfire_transaction_add_line(&lines, width,
416 _("Package"),
417 _("Arch"),
418 _("Version"),
419 _("Repository"),
420 _("Size")
421 );
422 pakfire_transaction_add_separator(&lines, width);
423
424 Queue classes;
425 queue_init(&classes);
426
427 // Get all classes
428 transaction_classify(transaction->transaction, mode, &classes);
429
430 Queue pkgs;
431 queue_init(&pkgs);
432
433 /*
434 The classes queue now contains a list of all classes as a tuple of:
435 * The class type
436 * The number of packages in this class
437 * The from ID (for arch/vendor change)
438 * The to ID (for arch/vendor change)
439 */
440 for (int i = 0; i < classes.count; i += 4) {
441 Id class = classes.elements[i];
442 unsigned int count = classes.elements[i+1];
443
444 const char* from = pool_id2str(pool, classes.elements[i+2]);
445 const char* to = pool_id2str(pool, classes.elements[i+3]);
446
447 switch (class) {
448 case SOLVER_TRANSACTION_INSTALL:
449 if (count)
450 pakfire_string_format(headline, _("Installing %u packages:"), count);
451 else
452 pakfire_string_set(headline, _("Installing one package:"));
453 break;
454
455 case SOLVER_TRANSACTION_REINSTALLED:
456 if (count)
457 pakfire_string_format(headline, _("Reinstalling %u packages:"), count);
458 else
459 pakfire_string_set(headline, _("Reinstalling one package:"));
460 break;
461
462 case SOLVER_TRANSACTION_ERASE:
463 if (count)
464 pakfire_string_format(headline, _("Removing %u packages:"), count);
465 else
466 pakfire_string_set(headline, _("Removing one package:"));
467 break;
468
469 case SOLVER_TRANSACTION_UPGRADED:
470 if (count)
471 pakfire_string_format(headline, _("Updating %u packages:"), count);
472 else
473 pakfire_string_set(headline, _("Updating one package:"));
474 break;
475
476 case SOLVER_TRANSACTION_DOWNGRADED:
477 if (count)
478 pakfire_string_format(headline, _("Downgrading %u packages:"), count);
479 else
480 pakfire_string_set(headline, _("Downgrading one package:"));
481 break;
482
483 case SOLVER_TRANSACTION_CHANGED:
484 if (count)
485 pakfire_string_format(headline, _("Changing %u packages:"), count);
486 else
487 pakfire_string_set(headline, _("Changing one package:"));
488 break;
489
490 case SOLVER_TRANSACTION_ARCHCHANGE:
491 if (count)
492 pakfire_string_format(headline,
493 _("%u architecture changes from '%s' to '%s':"), count, from, to);
494 else
495 pakfire_string_format(headline,
496 _("One architecture change from '%s' to '%s':"), from, to);
497 break;
498
499 case SOLVER_TRANSACTION_VENDORCHANGE:
500 if (count)
501 pakfire_string_format(headline,
502 _("%u vendor changes from '%s' to '%s':"), count, from, to);
503 else
504 pakfire_string_format(headline,
505 _("One vendor change from '%s' to '%s':"), from, to);
506 break;
507
508 case SOLVER_TRANSACTION_IGNORE:
509 continue;
510 }
511
512 // Show what we are doing
513 pakfire_transaction_add_headline(&lines, width, headline);
514
515 // Fetch packages in this class
516 transaction_classify_pkgs(transaction->transaction, mode, class,
517 classes.elements[i+2], classes.elements[i+3], &pkgs);
518
519 // List all packages
520 for (int j = 0; j < pkgs.count; j++) {
521 struct pakfire_package* old_pkg = pakfire_package_create_from_solvable(
522 transaction->pakfire, pkgs.elements[j]);
523 struct pakfire_package* new_pkg = NULL;
524
525 switch (class) {
526 case SOLVER_TRANSACTION_UPGRADED:
527 case SOLVER_TRANSACTION_DOWNGRADED:
528 new_pkg = pakfire_package_create_from_solvable(transaction->pakfire,
529 transaction_obs_pkg(transaction->transaction, pkgs.elements[j]));
530
531 pakfire_transaction_add_package_change(&lines, width, old_pkg, new_pkg);
532 break;
533
534 default:
535 pakfire_transaction_add_package(&lines, width, old_pkg);
536 break;
537 }
538
539 pakfire_package_unref(old_pkg);
540 if (new_pkg)
541 pakfire_package_unref(new_pkg);
542 }
543
544 // Newline
545 pakfire_transaction_add_newline(&lines, width);
546 }
547
548 queue_free(&classes);
549 queue_free(&pkgs);
550
551 // Summary
552 pakfire_transaction_add_headline(&lines, width, _("Transaction Summary"));
553 pakfire_transaction_add_separator(&lines, width);
554
555 // How much do we need to download?
556 size_t downloadsize = pakfire_transaction_downloadsize(transaction);
557
558 if (downloadsize > 0)
559 pakfire_transaction_add_usage_line(&lines, width,
560 _("Total Download Size"), downloadsize);
561
562 // How much more space do we need?
563 ssize_t sizechange = pakfire_transaction_installsizechange(transaction);
564 pakfire_transaction_add_usage_line(&lines, width,
565 (sizechange >= 0) ? _("Installed Size") : _("Freed Size"), sizechange);
566
567 // Join all lines together
568 char* string = pakfire_transaction_join_lines(lines);
569
570 DEBUG(transaction->pakfire, "Transaction: %s\n", string);
571
572 // Free lines
573 if (lines) {
574 for (char** line = lines; *line; line++)
575 free(*line);
576 free(lines);
577 }
578
579 return string;
580 }
581
582 static int pakfire_transaction_verify(struct pakfire_transaction* transaction,
583 struct pakfire_package* pkg, struct pakfire_archive* archive) {
584 int r;
585
586 const char* nevra = pakfire_package_get_nevra(pkg);
587
588 // Nothing to do if this step does not have an archive
589 if (!archive) {
590 DEBUG(transaction->pakfire, "Package %s requires no archive\n", nevra);
591 return 0;
592 }
593
594 enum pakfire_digests digest_type = PAKFIRE_DIGEST_NONE;
595
596 // Fetch digest from package
597 const unsigned char* expected_digest = pakfire_package_get_digest(pkg, &digest_type);
598 if (!expected_digest) {
599 DEBUG(transaction->pakfire, "Package %s has no digest\n", nevra);
600 return 0;
601 }
602
603 unsigned char computed_digest[EVP_MAX_MD_SIZE];
604 size_t digest_length = 0;
605
606 // Compute digest of the archive
607 r = pakfire_archive_digest(archive, digest_type, computed_digest, &digest_length);
608 if (r) {
609 ERROR(transaction->pakfire, "Could not compute digest for %s: %m\n", nevra);
610 return r;
611 }
612
613 // Compare digests
614 r = CRYPTO_memcmp(computed_digest, expected_digest, digest_length);
615 if (r) {
616 ERROR(transaction->pakfire, "Digests of %s do not match\n", nevra);
617 return 1;
618 }
619
620 return 0;
621 }
622
623 static int pakfire_transaction_run_script(struct pakfire_transaction* transaction,
624 struct pakfire_db* db, const char* type, struct pakfire_package* pkg, struct pakfire_archive* archive) {
625 struct pakfire_scriptlet* scriptlet = NULL;
626
627 // Fetch scriptlet from archive if possible
628 if (archive)
629 scriptlet = pakfire_archive_get_scriptlet(archive, type);
630 else
631 scriptlet = pakfire_db_get_scriptlet(db, pkg, type);
632
633 // Nothing to do if there are no scriptlets
634 if (!scriptlet)
635 return 0;
636
637 // Execute the scriptlet
638 pakfire_scriptlet_execute(scriptlet);
639
640 pakfire_scriptlet_unref(scriptlet);
641
642 return 0;
643 }
644
645 static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
646 struct pakfire_package* pkg, struct pakfire_archive* archive) {
647 // Extract payload to the root of the Pakfire instance
648 int r = pakfire_archive_extract(archive, NULL);
649 if (r) {
650 ERROR(transaction->pakfire, "Could not extract package %s: %m\n",
651 pakfire_package_get_nevra(pkg));
652 return r;
653 }
654
655 // Is it necessary to call ldconfig?
656 struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive);
657 if (filelist) {
658 int need_ldconfig = pakfire_filelist_contains(filelist, "*/lib*.so.?");
659
660 // Update the runtime linker cache
661 if (need_ldconfig)
662 pakfire_execute_ldconfig(transaction->pakfire);
663
664 pakfire_filelist_unref(filelist);
665 }
666
667 return r;
668 }
669
670 static int pakfire_transaction_erase(struct pakfire_transaction* transaction,
671 struct pakfire_db* db, struct pakfire_package* pkg) {
672 struct pakfire_filelist* filelist = NULL;
673 int r;
674
675 // Fetch filelist
676 r = pakfire_db_package_filelist(db, &filelist, pkg);
677 if (r)
678 goto ERROR;
679
680 const size_t length = pakfire_filelist_size(filelist);
681
682 // Delete all files
683 for (unsigned int i = 0; i < length; i++) {
684 struct pakfire_file* file = pakfire_filelist_get(filelist, i);
685
686 // Remove the file
687 r = pakfire_file_remove(file);
688 pakfire_file_unref(file);
689
690 // Break on any errors
691 if (r)
692 goto ERROR;
693 }
694
695 // Update the runtime linker cache after all files have been removed
696 pakfire_execute_ldconfig(transaction->pakfire);
697
698 ERROR:
699 if (filelist)
700 pakfire_filelist_unref(filelist);
701
702 return r;
703 }
704
705 static const char* pakfire_action_type_string(enum pakfire_actions type) {
706 switch (type) {
707 case PAKFIRE_ACTION_NOOP:
708 return "NOOP";
709
710 case PAKFIRE_ACTION_VERIFY:
711 return "VERIFY";
712
713 case PAKFIRE_ACTION_EXECUTE:
714 return "EXECUTE";
715
716 case PAKFIRE_ACTION_PRETRANS:
717 return "PRETRANS";
718
719 case PAKFIRE_ACTION_POSTTRANS:
720 return "POSTTRANS";
721 }
722
723 return NULL;
724 }
725
726 static int pakfire_transaction_package_is_userinstalled(
727 struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
728 // No packages on the list
729 if (!transaction->userinstalled)
730 return 0;
731
732 const char* name = pakfire_package_get_name(pkg);
733
734 // Check if the package is on the list
735 for (char** elem = transaction->userinstalled; *elem; elem++) {
736 if (strcmp(name, *elem) == 0)
737 return 1;
738 }
739
740 // Not found
741 return 0;
742 }
743
744 static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
745 struct pakfire_db* db, const enum pakfire_actions action, struct pakfire_package* pkg, struct pakfire_archive* archive) {
746 if (!pkg) {
747 errno = EINVAL;
748 return 1;
749 }
750
751 DEBUG(transaction->pakfire, "Running %s for %s\n",
752 pakfire_action_type_string(action), pakfire_package_get_nevra(pkg));
753
754 enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
755
756 int r = 0;
757 switch (action) {
758 // Verify this step
759 case PAKFIRE_ACTION_VERIFY:
760 r = pakfire_transaction_verify(transaction, pkg, archive);
761 break;
762
763 // Run the pre-transaction scripts
764 case PAKFIRE_ACTION_PRETRANS:
765 switch (type) {
766 case PAKFIRE_STEP_INSTALL:
767 case PAKFIRE_STEP_REINSTALL:
768 r = pakfire_transaction_run_script(transaction, db,
769 "pretransin", pkg, archive);
770 break;
771
772 case PAKFIRE_STEP_UPGRADE:
773 case PAKFIRE_STEP_DOWNGRADE:
774 r = pakfire_transaction_run_script(transaction, db,
775 "pretransup", pkg, archive);
776 break;
777
778 case PAKFIRE_STEP_ERASE:
779 case PAKFIRE_STEP_OBSOLETE:
780 r = pakfire_transaction_run_script(transaction, db,
781 "pretransun", pkg, archive);
782 break;
783
784 case PAKFIRE_STEP_IGNORE:
785 break;
786 }
787 break;
788
789 // Run the post-transaction scripts
790 case PAKFIRE_ACTION_POSTTRANS:
791 switch (type) {
792 case PAKFIRE_STEP_INSTALL:
793 case PAKFIRE_STEP_REINSTALL:
794 r = pakfire_transaction_run_script(transaction, db,
795 "posttransin", pkg, archive);
796 break;
797
798 case PAKFIRE_STEP_UPGRADE:
799 case PAKFIRE_STEP_DOWNGRADE:
800 r = pakfire_transaction_run_script(transaction, db,
801 "posttransup", pkg, archive);
802 break;
803
804 case PAKFIRE_STEP_ERASE:
805 case PAKFIRE_STEP_OBSOLETE:
806 r = pakfire_transaction_run_script(transaction, db,
807 "posttransun", pkg, archive);
808 break;
809
810 case PAKFIRE_STEP_IGNORE:
811 break;
812 }
813 break;
814
815 // Execute the action of this script
816 case PAKFIRE_ACTION_EXECUTE:
817 switch (type) {
818 case PAKFIRE_STEP_INSTALL:
819 case PAKFIRE_STEP_REINSTALL:
820 r = pakfire_transaction_run_script(transaction, db,
821 "prein", pkg, archive);
822 if (r)
823 break;
824
825 r = pakfire_transaction_extract(transaction, pkg, archive);
826 if (r)
827 break;
828
829 // Remove package metadata first when reinstalling
830 if (type == PAKFIRE_STEP_REINSTALL) {
831 r = pakfire_db_remove_package(db, pkg);
832 if (r)
833 break;
834 }
835
836 r = pakfire_db_add_package(db, pkg, archive,
837 pakfire_transaction_package_is_userinstalled(transaction, pkg));
838 if (r)
839 break;
840
841 r = pakfire_transaction_run_script(transaction, db,
842 "postin", pkg, archive);
843 break;
844
845 case PAKFIRE_STEP_UPGRADE:
846 case PAKFIRE_STEP_DOWNGRADE:
847 r = pakfire_transaction_run_script(transaction, db,
848 "preup", pkg, archive);
849 if (r)
850 break;
851
852 r = pakfire_transaction_extract(transaction, pkg, archive);
853 if (r)
854 break;
855
856 r = pakfire_db_add_package(db, pkg, archive,
857 pakfire_transaction_package_is_userinstalled(transaction, pkg));
858 if (r)
859 break;
860
861 r = pakfire_transaction_run_script(transaction, db,
862 "postup", pkg, archive);
863 break;
864
865 case PAKFIRE_STEP_ERASE:
866 case PAKFIRE_STEP_OBSOLETE:
867 r = pakfire_transaction_run_script(transaction, db,
868 "preun", pkg, archive);
869 if (r)
870 break;
871
872 r = pakfire_transaction_erase(transaction, db, pkg);
873 if (r)
874 break;
875
876 r = pakfire_db_remove_package(db, pkg);
877 if (r)
878 break;
879
880 r = pakfire_transaction_run_script(transaction, db,
881 "postun", pkg, archive);
882 break;
883
884 case PAKFIRE_STEP_IGNORE:
885 break;
886 }
887 break;
888
889 // Do nothing
890 case PAKFIRE_ACTION_NOOP:
891 break;
892 }
893
894 if (r)
895 ERROR(transaction->pakfire, "Step has failed: %s\n", strerror(r));
896
897 return r;
898 }
899
900 static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction,
901 struct pakfire_db* db, enum pakfire_actions action) {
902 int r = 0;
903
904 // Walk through all steps
905 for (unsigned int i = 0; i < transaction->num; i++) {
906 r = pakfire_transaction_run_step(transaction, db, action,
907 transaction->packages[i], transaction->archives[i]);
908
909 // End loop if action was unsuccessful
910 if (r) {
911 DEBUG(transaction->pakfire, "Step %d failed: %m\n", i);
912 break;
913 }
914 }
915
916 return r;
917 }
918
919 static int pakfire_transaction_open_archives(struct pakfire_transaction* transaction) {
920 for (unsigned int i = 0; i < transaction->num; i++) {
921 struct pakfire_package* pkg = transaction->packages[i];
922
923 // Fetch the type
924 enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
925
926 // Do we need the archive?
927 switch (type) {
928 case PAKFIRE_STEP_INSTALL:
929 case PAKFIRE_STEP_REINSTALL:
930 case PAKFIRE_STEP_UPGRADE:
931 case PAKFIRE_STEP_DOWNGRADE:
932 case PAKFIRE_STEP_OBSOLETE:
933 break;
934
935 case PAKFIRE_STEP_ERASE:
936 case PAKFIRE_STEP_IGNORE:
937 continue;
938 }
939
940 transaction->archives[i] = pakfire_package_get_archive(pkg);
941 if (!transaction->archives[i]) {
942 ERROR(transaction->pakfire, "Could not open archive for %s: %m\n",
943 pakfire_package_get_nevra(pkg));
944 return 1;
945 }
946 }
947
948 return 0;
949 }
950
951 static int pakfire_transaction_perform(struct pakfire_transaction* transaction) {
952 struct pakfire_repo* repo = NULL;
953 struct pakfire_db* db;
954 int r;
955
956 DEBUG(transaction->pakfire, "Running Transaction %p\n", transaction);
957
958 // Open all archives
959 r = pakfire_transaction_open_archives(transaction);
960 if (r)
961 return r;
962
963 // Open the database
964 r = pakfire_db_open(&db, transaction->pakfire, PAKFIRE_DB_READWRITE);
965 if (r) {
966 ERROR(transaction->pakfire, "Could not open the database\n");
967 return r;
968 }
969
970 // Verify steps
971 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_VERIFY);
972 if (r)
973 goto ERROR;
974
975 // Execute all pre transaction actions
976 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_PRETRANS);
977 if (r)
978 goto ERROR;
979
980 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_EXECUTE);
981 if (r)
982 goto ERROR;
983
984 // Execute all post transaction actions
985 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_POSTTRANS);
986 if (r)
987 goto ERROR;
988
989 DEBUG(transaction->pakfire, "The transaction has finished successfully\n");
990
991 // Reload database for next transaction
992
993 repo = pakfire_get_installed_repo(transaction->pakfire);
994 if (!repo)
995 goto ERROR;
996
997 // Reload the database
998 r = pakfire_db_load(db, repo);
999
1000 ERROR:
1001 if (repo)
1002 pakfire_repo_unref(repo);
1003 pakfire_db_unref(db);
1004
1005 return r;
1006 }
1007
1008 static int pakfire_transaction_download_package(struct pakfire_transaction* transaction,
1009 struct pakfire_downloader* downloader, struct pakfire_package* pkg) {
1010 int r = 1;
1011 struct pakfire_repo* repo = NULL;
1012 struct pakfire_mirrorlist* mirrorlist = NULL;
1013
1014 // Fetch the repository to download from
1015 repo = pakfire_package_get_repo(pkg);
1016 if (!repo)
1017 goto ERROR;
1018
1019 // Fetch baseurl
1020 const char* baseurl = pakfire_repo_get_baseurl(repo);
1021
1022 // Fetch mirrorlist
1023 mirrorlist = pakfire_repo_get_mirrorlist(repo);
1024
1025 const char* nevra = pakfire_package_get_nevra(pkg);
1026 if (!nevra)
1027 goto ERROR;
1028
1029 // Where to store the package?
1030 const char* path = pakfire_package_get_path(pkg);
1031 if (!path) {
1032 ERROR(transaction->pakfire, "Could not retrieve package path for %s: %m\n", nevra);
1033 goto ERROR;
1034 }
1035
1036 // What file to download?
1037 const char* filename = pakfire_package_get_filename(pkg);
1038 if (!filename) {
1039 ERROR(transaction->pakfire, "Could not retrieve filename for package %s: %m\n", nevra);
1040 goto ERROR;
1041 }
1042
1043 enum pakfire_digests digest_type = PAKFIRE_DIGEST_NONE;
1044
1045 // Retrieve package digest
1046 const unsigned char* digest = pakfire_package_get_digest(pkg, &digest_type);
1047 const size_t digest_length = pakfire_digest_length(digest_type);
1048
1049 // Add transfer to downloader
1050 r = pakfire_downloader_add_transfer(downloader, baseurl, mirrorlist,
1051 nevra, filename, path, digest_type, digest, digest_length, 0);
1052
1053 ERROR:
1054 if (mirrorlist)
1055 pakfire_mirrorlist_unref(mirrorlist);
1056 if (repo)
1057 pakfire_repo_unref(repo);
1058
1059 return r;
1060 }
1061
1062 static int pakfire_transaction_package_needs_download(
1063 struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
1064 enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
1065 switch (type) {
1066 case PAKFIRE_STEP_INSTALL:
1067 case PAKFIRE_STEP_REINSTALL:
1068 case PAKFIRE_STEP_DOWNGRADE:
1069 case PAKFIRE_STEP_UPGRADE:
1070 break;
1071
1072 // No need to download for these steps
1073 default:
1074 return 0;
1075 }
1076
1077 // No download required if this package is already installed
1078 if (pakfire_package_is_installed(pkg))
1079 return 0;
1080
1081 const char* path = pakfire_package_get_path(pkg);
1082
1083 // Does the file exist?
1084 int r = access(path, R_OK);
1085 if (r == 0)
1086 return 0;
1087
1088 // This package needs to be downloaded
1089 return 1;
1090 }
1091
1092 PAKFIRE_EXPORT int pakfire_transaction_download(struct pakfire_transaction* transaction) {
1093 struct pakfire_downloader* downloader;
1094 int r;
1095
1096 // Initialize the downloader
1097 r = pakfire_downloader_create(&downloader, transaction->pakfire);
1098 if (r) {
1099 ERROR(transaction->pakfire, "Could not initialize downloader: %m\n");
1100 return 1;
1101 }
1102
1103 // Add all packages that need to be downloaded
1104 for (unsigned int i = 0; i < transaction->num; i++) {
1105 struct pakfire_package* pkg = transaction->packages[i];
1106
1107 if (!pakfire_transaction_package_needs_download(transaction, pkg))
1108 continue;
1109
1110 // Enqueue download
1111 r = pakfire_transaction_download_package(transaction, downloader, pkg);
1112 if (r) {
1113 const char* nevra = pakfire_package_get_nevra(pkg);
1114
1115 ERROR(transaction->pakfire, "Could not add download to queue: %s: %m\n", nevra);
1116 goto ERROR;
1117 }
1118 }
1119
1120 // Run the downloader
1121 r = pakfire_downloader_run(downloader);
1122
1123 ERROR:
1124 pakfire_downloader_unref(downloader);
1125
1126 return r;
1127 }
1128
1129 PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transaction) {
1130 int r;
1131
1132 // Skip running an empty transaction
1133 if (!transaction->num) {
1134 DEBUG(transaction->pakfire, "Empty transaction. Skipping...\n");
1135 return 0;
1136 }
1137
1138 // Show what would be done
1139 char* dump = pakfire_transaction_dump(transaction, 80);
1140
1141 // Check if we should continue
1142 r = pakfire_ui_confirm(transaction->pakfire, dump, _("Is this okay? [y/N]"));
1143 if (r) {
1144 ERROR(transaction->pakfire, "Transaction aborted upon user request\n");
1145 goto ERROR;
1146 }
1147
1148 // Write transaction dump to log
1149 INFO(transaction->pakfire, "%s\n", dump);
1150
1151 // Download what we need
1152 r = pakfire_transaction_download(transaction);
1153 if (r)
1154 goto ERROR;
1155
1156 // Perform all steps
1157 r = pakfire_transaction_perform(transaction);
1158 if (r)
1159 goto ERROR;
1160
1161 ERROR:
1162 free(dump);
1163
1164 return r;
1165 }