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