]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/transaction.c
filelist: Change type from PakfireFilelist to struct pakfire_filelist
[people/ms/pakfire.git] / src / libpakfire / transaction.c
CommitLineData
221cc3ce
MT
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
751ed354 21#include <errno.h>
f0d6233d
MT
22#include <stdlib.h>
23
221cc3ce
MT
24#include <solv/transaction.h>
25
f52c8e7c 26#include <pakfire/archive.h>
a70547e2 27#include <pakfire/db.h>
751ed354 28#include <pakfire/downloader.h>
f52c8e7c 29#include <pakfire/execute.h>
ac71886a
MT
30#include <pakfire/file.h>
31#include <pakfire/filelist.h>
b2c4f381 32#include <pakfire/i18n.h>
2ee86b5f 33#include <pakfire/logging.h>
221cc3ce 34#include <pakfire/package.h>
aa042cfc 35#include <pakfire/pakfire.h>
9f953e68 36#include <pakfire/private.h>
b2c4f381 37#include <pakfire/repo.h>
221cc3ce
MT
38#include <pakfire/transaction.h>
39#include <pakfire/types.h>
be6d221a 40#include <pakfire/ui.h>
221cc3ce
MT
41#include <pakfire/util.h>
42
d94fa31e 43struct pakfire_transaction {
aa042cfc 44 Pakfire pakfire;
40fd1f3f
MT
45 int nrefs;
46
2ee86b5f 47 Transaction* transaction;
41ebf5f0 48 char** userinstalled;
40fd1f3f 49
900faa2f 50 struct pakfire_archive** archives;
31480bee 51 struct pakfire_package** packages;
f52c8e7c 52 size_t num;
2ee86b5f
MT
53};
54
3d2d7113
MT
55enum 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
63enum 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
73static enum pakfire_steps pakfire_transaction_get_step_type(
31480bee 74 struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
96c6df88
MT
75 int type = transaction_type(transaction->transaction, pakfire_package_id(pkg),
76 SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_CHANGE_IS_REINSTALL);
6a473d43
MT
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
f52c8e7c
MT
109static 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;
40fd1f3f 118 }
6cdfb9dd 119
f52c8e7c
MT
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
130static void pakfire_transaction_free(struct pakfire_transaction* transaction) {
131 pakfire_transaction_free_archives_and_packages(transaction);
132
41ebf5f0
MT
133 if (transaction->userinstalled) {
134 for (char** userinstalled = transaction->userinstalled; *userinstalled; userinstalled++)
135 free(*userinstalled);
dc0cae14 136 free(transaction->userinstalled);
41ebf5f0 137 }
40fd1f3f 138 transaction_free(transaction->transaction);
6cdfb9dd 139
40fd1f3f
MT
140 pakfire_unref(transaction->pakfire);
141 free(transaction);
142}
dff5fb7f 143
f52c8e7c 144static int pakfire_transaction_import_transaction(
41ebf5f0
MT
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;
dff5fb7f 152
f52c8e7c
MT
153 // Order the transaction
154 transaction_order(t, 0);
6a473d43 155
41ebf5f0
MT
156 // Free any previous content
157 pakfire_transaction_free_archives_and_packages(transaction);
158
f52c8e7c
MT
159 // How many steps?
160 transaction->num = t->steps.count;
6a473d43 161
f52c8e7c
MT
162 // Allocate space for packages
163 transaction->packages = calloc(transaction->num, sizeof(*transaction->packages));
164 if (!transaction->packages)
40fd1f3f 165 return 1;
6a473d43 166
f52c8e7c
MT
167 // Allocate space for archives
168 transaction->archives = calloc(transaction->num, sizeof(*transaction->archives));
169 if (!transaction->archives)
170 return 1;
221cc3ce 171
f52c8e7c
MT
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 }
7214135e 177
f52c8e7c 178 return 0;
2ee86b5f
MT
179}
180
dc0cae14 181static int pakfire_transaction_import_userinstalled(
41ebf5f0 182 struct pakfire_transaction* t, Solver* solver) {
dc0cae14
MT
183 if (t->userinstalled) {
184 free(t->userinstalled);
185 t->userinstalled = NULL;
186 }
187
41ebf5f0
MT
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
dc0cae14 194 // Skip everything if the queue is empty
41ebf5f0 195 if (!userinstalled.count)
dc0cae14
MT
196 return 0;
197
41ebf5f0 198 t->userinstalled = calloc(userinstalled.count + 1, sizeof(*t->userinstalled));
dc0cae14
MT
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
41ebf5f0
MT
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 }
dc0cae14
MT
211
212 return 0;
213}
214
d94fa31e 215int pakfire_transaction_create(struct pakfire_transaction** transaction,
41ebf5f0 216 Pakfire pakfire, Solver* solver) {
d94fa31e 217 struct pakfire_transaction* t = calloc(1, sizeof(*t));
40fd1f3f
MT
218 if (!t)
219 return ENOMEM;
2ee86b5f 220
40fd1f3f
MT
221 // Store reference to Pakfire
222 t->pakfire = pakfire_ref(pakfire);
dff5fb7f 223
40fd1f3f
MT
224 // Initialize the reference counter
225 t->nrefs = 1;
7214135e 226
f52c8e7c 227 // Import transaction
41ebf5f0 228 int r = pakfire_transaction_import_transaction(t, solver);
f52c8e7c
MT
229 if (r)
230 goto ERROR;
40fd1f3f 231
dc0cae14 232 // Import userinstalled
41ebf5f0 233 r = pakfire_transaction_import_userinstalled(t, solver);
dc0cae14
MT
234 if (r)
235 goto ERROR;
236
40fd1f3f
MT
237 *transaction = t;
238 return 0;
239
240ERROR:
241 pakfire_transaction_free(t);
40fd1f3f
MT
242 return 1;
243}
244
f52c8e7c
MT
245PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_ref(
246 struct pakfire_transaction* transaction) {
40fd1f3f
MT
247 transaction->nrefs++;
248
249 return transaction;
221cc3ce
MT
250}
251
f52c8e7c
MT
252PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_unref(
253 struct pakfire_transaction* transaction) {
2ee86b5f
MT
254 if (--transaction->nrefs > 0)
255 return transaction;
256
257 pakfire_transaction_free(transaction);
258 return NULL;
259}
260
d94fa31e 261PAKFIRE_EXPORT size_t pakfire_transaction_count(struct pakfire_transaction* transaction) {
f52c8e7c 262 return transaction->num;
221cc3ce
MT
263}
264
d94fa31e 265static ssize_t pakfire_transaction_installsizechange(struct pakfire_transaction* transaction) {
b2c4f381 266 ssize_t sizechange = transaction_calc_installsizechange(transaction->transaction);
221cc3ce
MT
267
268 // Convert from kbytes to bytes
269 return sizechange * 1024;
270}
271
d94fa31e 272PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
b2c4f381
MT
273 ssize_t size = 0;
274
f52c8e7c
MT
275 for (unsigned int i = 0; i < transaction->num; i++)
276 size += pakfire_package_get_downloadsize(transaction->packages[i]);
b2c4f381
MT
277
278 return size;
279}
280
1edcb999
MT
281static 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
313static void pakfire_transaction_add_headline(char*** lines, size_t width, const char* headline) {
314 pakfire_transaction_append_line(lines, "%s\n", headline);
b2c4f381
MT
315}
316
1edcb999
MT
317static void pakfire_transaction_add_newline(char*** lines, size_t width) {
318 pakfire_transaction_append_line(lines, "\n");
b2c4f381
MT
319}
320
1edcb999 321static void pakfire_transaction_add_line(char*** lines, size_t width, const char* name,
b2c4f381 322 const char* arch, const char* version, const char* repo, const char* size) {
1edcb999 323 pakfire_transaction_append_line(lines, " %-21s %-8s %-21s %-18s %6s \n",
9a649d7c 324 name, arch, version, repo, size);
b2c4f381
MT
325}
326
31480bee 327static void pakfire_transaction_add_package(char*** lines, size_t width, struct pakfire_package* pkg) {
cc90545a
MT
328 char size[128];
329
4651122b 330 struct pakfire_repo* repo = pakfire_package_get_repo(pkg);
b2c4f381 331
cc90545a
MT
332 // Format size
333 pakfire_format_size(size, sizeof(size) - 1, pakfire_package_get_size(pkg));
b2c4f381 334
1edcb999 335 pakfire_transaction_add_line(lines, width,
b2c4f381
MT
336 pakfire_package_get_name(pkg),
337 pakfire_package_get_arch(pkg),
338 pakfire_package_get_evr(pkg),
339 pakfire_repo_get_name(repo),
cc90545a 340 size
b2c4f381
MT
341 );
342
3ff6aee6 343 pakfire_repo_unref(repo);
b2c4f381
MT
344}
345
1edcb999 346static void pakfire_transaction_add_package_change(char*** lines, size_t width,
31480bee 347 struct pakfire_package* old_pkg, struct pakfire_package* new_pkg) {
40fd1f3f 348 // Print the new package first
1edcb999 349 pakfire_transaction_add_package(lines, width, new_pkg);
40fd1f3f 350
1edcb999
MT
351 pakfire_transaction_append_line(lines,
352 " --> %s\n", pakfire_package_get_nevra(old_pkg));
40fd1f3f
MT
353}
354
1edcb999 355static void pakfire_transaction_add_separator(char*** lines, size_t width) {
9a649d7c
MT
356 char* separator = alloca(width + 1);
357
9a649d7c
MT
358 for (unsigned int i = 0; i < width; i++)
359 separator[i] = '=';
360 separator[width] = '\0';
b2c4f381 361
1edcb999 362 pakfire_transaction_append_line(lines, "%s\n", separator);
b2c4f381
MT
363}
364
1edcb999
MT
365static void pakfire_transaction_add_usage_line(char*** lines, size_t width,
366 const char* headline, ssize_t size) {
cc90545a
MT
367 char buffer[128];
368 pakfire_format_size(buffer, sizeof(buffer) - 1, size);
b2c4f381 369
1edcb999
MT
370 pakfire_transaction_append_line(lines, "%-21s: %s\n", headline, buffer);
371}
372
373static 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;
b2c4f381
MT
395}
396
d94fa31e 397PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transaction, size_t width) {
40fd1f3f 398 char headline[1024];
b2c4f381 399
40fd1f3f
MT
400 Pool* pool = transaction->transaction->pool;
401 const int mode =
402 SOLVER_TRANSACTION_SHOW_OBSOLETES |
403 SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
404
1edcb999
MT
405 char** lines = NULL;
406
b2c4f381 407 // Header
1edcb999
MT
408 pakfire_transaction_add_separator(&lines, width);
409 pakfire_transaction_add_line(&lines, width,
b2c4f381
MT
410 _("Package"),
411 _("Arch"),
412 _("Version"),
413 _("Repository"),
414 _("Size")
415 );
1edcb999 416 pakfire_transaction_add_separator(&lines, width);
b2c4f381 417
40fd1f3f
MT
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
1edcb999 507 pakfire_transaction_add_headline(&lines, width, headline);
40fd1f3f
MT
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++) {
31480bee 515 struct pakfire_package* old_pkg = pakfire_package_create_from_solvable(
40fd1f3f 516 transaction->pakfire, pkgs.elements[j]);
31480bee 517 struct pakfire_package* new_pkg = NULL;
40fd1f3f
MT
518
519 switch (class) {
520 case SOLVER_TRANSACTION_UPGRADED:
521 case SOLVER_TRANSACTION_DOWNGRADED:
23c48feb 522 new_pkg = pakfire_package_create_from_solvable(transaction->pakfire,
40fd1f3f
MT
523 transaction_obs_pkg(transaction->transaction, pkgs.elements[j]));
524
1edcb999 525 pakfire_transaction_add_package_change(&lines, width, old_pkg, new_pkg);
40fd1f3f
MT
526 break;
527
528 default:
1edcb999 529 pakfire_transaction_add_package(&lines, width, old_pkg);
40fd1f3f
MT
530 break;
531 }
532
23c48feb
MT
533 pakfire_package_unref(old_pkg);
534 if (new_pkg)
535 pakfire_package_unref(new_pkg);
40fd1f3f
MT
536 }
537
538 // Newline
1edcb999 539 pakfire_transaction_add_newline(&lines, width);
40fd1f3f
MT
540 }
541
542 queue_free(&classes);
543 queue_free(&pkgs);
b2c4f381
MT
544
545 // Summary
1edcb999
MT
546 pakfire_transaction_add_headline(&lines, width, _("Transaction Summary"));
547 pakfire_transaction_add_separator(&lines, width);
b2c4f381 548
b2c4f381
MT
549 // How much do we need to download?
550 size_t downloadsize = pakfire_transaction_downloadsize(transaction);
40fd1f3f 551
b2c4f381 552 if (downloadsize > 0)
1edcb999 553 pakfire_transaction_add_usage_line(&lines, width,
b2c4f381
MT
554 _("Total Download Size"), downloadsize);
555
556 // How much more space do we need?
557 ssize_t sizechange = pakfire_transaction_installsizechange(transaction);
1edcb999 558 pakfire_transaction_add_usage_line(&lines, width,
b2c4f381
MT
559 (sizechange >= 0) ? _("Installed Size") : _("Freed Size"), sizechange);
560
1edcb999
MT
561 // Join all lines together
562 char* string = pakfire_transaction_join_lines(lines);
b2c4f381 563
12656820 564 DEBUG(transaction->pakfire, "Transaction: %s\n", string);
2ee86b5f 565
1edcb999
MT
566 // Free lines
567 if (lines) {
568 for (char** line = lines; *line; line++)
569 free(*line);
570 free(lines);
571 }
572
b2c4f381
MT
573 return string;
574}
dea70c9f 575
f52c8e7c 576static int pakfire_transaction_verify(struct pakfire_transaction* transaction,
900faa2f 577 struct pakfire_package* pkg, struct pakfire_archive* archive) {
f52c8e7c
MT
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
594static int pakfire_transaction_run_script(struct pakfire_transaction* transaction,
900faa2f 595 struct pakfire_db* db, const char* type, struct pakfire_package* pkg, struct pakfire_archive* archive) {
f52c8e7c
MT
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
a0097ba2
MT
608 // Execute the scriptlet
609 pakfire_scriptlet_execute(scriptlet);
f52c8e7c 610
a0097ba2 611 pakfire_scriptlet_unref(scriptlet);
f52c8e7c
MT
612
613 return 0;
614}
615
616static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
900faa2f 617 struct pakfire_package* pkg, struct pakfire_archive* archive) {
f52c8e7c
MT
618 // Extract payload to the root of the Pakfire instance
619 int r = pakfire_archive_extract(archive, NULL);
620 if (r) {
b1772bfb
MT
621 ERROR(transaction->pakfire, "Could not extract package %s: %m\n",
622 pakfire_package_get_nevra(pkg));
f52c8e7c
MT
623 return r;
624 }
625
626 // Is it necessary to call ldconfig?
1bbbfb9e 627 struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive);
f52c8e7c
MT
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
641static int pakfire_transaction_erase(struct pakfire_transaction* transaction,
31480bee 642 struct pakfire_db* db, struct pakfire_package* pkg) {
1bbbfb9e 643 struct pakfire_filelist* filelist = NULL;
ac71886a
MT
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
f52c8e7c
MT
666 // Update the runtime linker cache after all files have been removed
667 pakfire_execute_ldconfig(transaction->pakfire);
668
ac71886a
MT
669ERROR:
670 if (filelist)
671 pakfire_filelist_unref(filelist);
672
673 return r;
f52c8e7c
MT
674}
675
3d2d7113 676static const char* pakfire_action_type_string(enum pakfire_actions type) {
f52c8e7c
MT
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
dc0cae14 697static int pakfire_transaction_package_is_userinstalled(
31480bee 698 struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
dc0cae14
MT
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
41ebf5f0 706 for (char** elem = transaction->userinstalled; *elem; elem++) {
dc0cae14
MT
707 if (strcmp(name, *elem) == 0)
708 return 1;
709 }
710
711 // Not found
712 return 0;
713}
714
f52c8e7c 715static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
900faa2f 716 struct pakfire_db* db, const enum pakfire_actions action, struct pakfire_package* pkg, struct pakfire_archive* archive) {
f52c8e7c
MT
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
3d2d7113 725 enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
f52c8e7c
MT
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,
a0097ba2 740 "pretransin", pkg, archive);
f52c8e7c
MT
741 break;
742
743 case PAKFIRE_STEP_UPGRADE:
744 case PAKFIRE_STEP_DOWNGRADE:
745 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 746 "pretransup", pkg, archive);
f52c8e7c
MT
747 break;
748
749 case PAKFIRE_STEP_ERASE:
750 case PAKFIRE_STEP_OBSOLETE:
751 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 752 "pretransun", pkg, archive);
f52c8e7c
MT
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,
a0097ba2 766 "posttransin", pkg, archive);
f52c8e7c
MT
767 break;
768
769 case PAKFIRE_STEP_UPGRADE:
770 case PAKFIRE_STEP_DOWNGRADE:
771 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 772 "posttransup", pkg, archive);
f52c8e7c
MT
773 break;
774
775 case PAKFIRE_STEP_ERASE:
776 case PAKFIRE_STEP_OBSOLETE:
777 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 778 "posttransun", pkg, archive);
f52c8e7c
MT
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,
a0097ba2 792 "prein", pkg, archive);
f52c8e7c
MT
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
dc0cae14
MT
807 r = pakfire_db_add_package(db, pkg, archive,
808 pakfire_transaction_package_is_userinstalled(transaction, pkg));
f52c8e7c
MT
809 if (r)
810 break;
811
812 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 813 "postin", pkg, archive);
f52c8e7c
MT
814 break;
815
816 case PAKFIRE_STEP_UPGRADE:
817 case PAKFIRE_STEP_DOWNGRADE:
818 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 819 "preup", pkg, archive);
f52c8e7c
MT
820 if (r)
821 break;
822
823 r = pakfire_transaction_extract(transaction, pkg, archive);
824 if (r)
825 break;
826
dc0cae14
MT
827 r = pakfire_db_add_package(db, pkg, archive,
828 pakfire_transaction_package_is_userinstalled(transaction, pkg));
f52c8e7c
MT
829 if (r)
830 break;
831
832 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 833 "postup", pkg, archive);
f52c8e7c
MT
834 break;
835
836 case PAKFIRE_STEP_ERASE:
837 case PAKFIRE_STEP_OBSOLETE:
838 r = pakfire_transaction_run_script(transaction, db,
a0097ba2 839 "preun", pkg, archive);
f52c8e7c
MT
840 if (r)
841 break;
842
ac71886a 843 r = pakfire_transaction_erase(transaction, db, pkg);
f52c8e7c
MT
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,
a0097ba2 852 "postun", pkg, archive);
f52c8e7c
MT
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
d94fa31e 871static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction,
3d2d7113 872 struct pakfire_db* db, enum pakfire_actions action) {
dff5fb7f 873 int r = 0;
dea70c9f
MT
874
875 // Walk through all steps
f52c8e7c
MT
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]);
dea70c9f 879
dea70c9f 880 // End loop if action was unsuccessful
a1a7f924 881 if (r) {
b1772bfb 882 DEBUG(transaction->pakfire, "Step %d failed: %m\n", i);
dea70c9f 883 break;
a1a7f924 884 }
dea70c9f
MT
885 }
886
887 return r;
888}
889
f52c8e7c
MT
890static int pakfire_transaction_open_archives(struct pakfire_transaction* transaction) {
891 for (unsigned int i = 0; i < transaction->num; i++) {
31480bee 892 struct pakfire_package* pkg = transaction->packages[i];
f52c8e7c
MT
893
894 // Fetch the type
3d2d7113 895 enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
f52c8e7c
MT
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]) {
b1772bfb
MT
913 ERROR(transaction->pakfire, "Could not open archive for %s: %m\n",
914 pakfire_package_get_nevra(pkg));
f52c8e7c
MT
915 return 1;
916 }
917 }
918
919 return 0;
920}
921
b0fa672d 922static int pakfire_transaction_perform(struct pakfire_transaction* transaction) {
4651122b 923 struct pakfire_repo* repo = NULL;
a70547e2
MT
924 struct pakfire_db* db;
925 int r;
926
12656820 927 DEBUG(transaction->pakfire, "Running Transaction %p\n", transaction);
a70547e2 928
f52c8e7c
MT
929 // Open all archives
930 r = pakfire_transaction_open_archives(transaction);
931 if (r)
932 return r;
933
a70547e2 934 // Open the database
77a4b3a3 935 r = pakfire_db_open(&db, transaction->pakfire, PAKFIRE_DB_READWRITE);
a70547e2
MT
936 if (r) {
937 ERROR(transaction->pakfire, "Could not open the database\n");
938 return r;
939 }
dea70c9f
MT
940
941 // Verify steps
76a9f5dc 942 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_VERIFY);
dea70c9f 943 if (r)
a70547e2 944 goto ERROR;
dea70c9f
MT
945
946 // Execute all pre transaction actions
76a9f5dc 947 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_PRETRANS);
dea70c9f 948 if (r)
a1a7f924 949 goto ERROR;
dea70c9f 950
76a9f5dc 951 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_EXECUTE);
dea70c9f 952 if (r)
a1a7f924 953 goto ERROR;
dea70c9f
MT
954
955 // Execute all post transaction actions
76a9f5dc 956 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_POSTTRANS);
dea70c9f 957 if (r)
a1a7f924 958 goto ERROR;
dea70c9f 959
a70547e2 960 DEBUG(transaction->pakfire, "The transaction has finished successfully\n");
a1a7f924 961
2bfcf0bf
MT
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
a1a7f924 971ERROR:
2bfcf0bf
MT
972 if (repo)
973 pakfire_repo_unref(repo);
a70547e2 974 pakfire_db_unref(db);
a1a7f924
MT
975
976 return r;
dea70c9f 977}
4a81ff85 978
d94fa31e 979static int pakfire_transaction_download_package(struct pakfire_transaction* transaction,
31480bee 980 struct pakfire_downloader* downloader, struct pakfire_package* pkg) {
751ed354 981 int r = 1;
4651122b 982 struct pakfire_repo* repo = NULL;
327b0c89 983 struct pakfire_mirrorlist* mirrorlist = NULL;
751ed354
MT
984
985 // Fetch the repository to download from
986 repo = pakfire_package_get_repo(pkg);
987 if (!repo)
988 goto ERROR;
989
8960e0a2
MT
990 // Fetch baseurl
991 const char* baseurl = pakfire_repo_get_baseurl(repo);
992
751ed354 993 // Fetch mirrorlist
327b0c89 994 mirrorlist = pakfire_repo_get_mirrorlist(repo);
751ed354
MT
995
996 // Where to store the package?
b12a5d7d
MT
997 const char* path = pakfire_package_get_path(pkg);
998 if (!path)
751ed354
MT
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
40fd1f3f 1006 const char* nevra = pakfire_package_get_nevra(pkg);
e60f98d5
MT
1007 if (!nevra)
1008 goto ERROR;
1009
751ed354 1010 // Add transfer to downloader
e60f98d5 1011 r = pakfire_downloader_add_transfer(downloader, baseurl, mirrorlist,
b12a5d7d 1012 nevra, filename, path, 0);
751ed354
MT
1013
1014ERROR:
327b0c89
MT
1015 if (mirrorlist)
1016 pakfire_mirrorlist_unref(mirrorlist);
751ed354
MT
1017 if (repo)
1018 pakfire_repo_unref(repo);
1019
1020 return r;
1021}
1022
cde9923f 1023static int pakfire_transaction_package_needs_download(
31480bee 1024 struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
3d2d7113 1025 enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
cde9923f
MT
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
d94fa31e 1053PAKFIRE_EXPORT int pakfire_transaction_download(struct pakfire_transaction* transaction) {
751ed354 1054 struct pakfire_downloader* downloader;
4a81ff85
MT
1055 int r;
1056
751ed354
MT
1057 // Initialize the downloader
1058 r = pakfire_downloader_create(&downloader, transaction->pakfire);
1059 if (r) {
b1772bfb 1060 ERROR(transaction->pakfire, "Could not initialize downloader: %m\n");
4a81ff85 1061 return 1;
751ed354 1062 }
4a81ff85
MT
1063
1064 // Add all packages that need to be downloaded
f52c8e7c 1065 for (unsigned int i = 0; i < transaction->num; i++) {
31480bee 1066 struct pakfire_package* pkg = transaction->packages[i];
f52c8e7c 1067
cde9923f
MT
1068 if (!pakfire_transaction_package_needs_download(transaction, pkg))
1069 continue;
4a81ff85 1070
751ed354
MT
1071 // Enqueue download
1072 r = pakfire_transaction_download_package(transaction, downloader, pkg);
4a81ff85
MT
1073 if (r)
1074 goto ERROR;
1075 }
1076
751ed354
MT
1077 // Run the downloader
1078 r = pakfire_downloader_run(downloader);
4a81ff85
MT
1079
1080ERROR:
751ed354 1081 pakfire_downloader_unref(downloader);
4a81ff85
MT
1082
1083 return r;
1084}
b0fa672d
MT
1085
1086PAKFIRE_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
be6d221a
MT
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
b0fa672d
MT
1108 // Download what we need
1109 r = pakfire_transaction_download(transaction);
1110 if (r)
be6d221a 1111 goto ERROR;
b0fa672d
MT
1112
1113 // Perform all steps
1114 r = pakfire_transaction_perform(transaction);
1115 if (r)
be6d221a 1116 goto ERROR;
b0fa672d 1117
be6d221a
MT
1118ERROR:
1119 free(dump);
1120
1121 return r;
b0fa672d 1122}