# #
#############################################################################*/
-#include <assert.h>
+#include <errno.h>
#include <stdlib.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+
+#include <solv/pool_fileconflicts.h>
#include <solv/transaction.h>
+#include <pakfire/archive.h>
#include <pakfire/db.h>
+#include <pakfire/digest.h>
+#include <pakfire/downloader.h>
+#include <pakfire/file.h>
+#include <pakfire/filelist.h>
#include <pakfire/i18n.h>
+#include <pakfire/jail.h>
#include <pakfire/logging.h>
#include <pakfire/package.h>
-#include <pakfire/packagelist.h>
#include <pakfire/pakfire.h>
#include <pakfire/private.h>
#include <pakfire/repo.h>
-#include <pakfire/step.h>
+#include <pakfire/string.h>
#include <pakfire/transaction.h>
-#include <pakfire/types.h>
+#include <pakfire/ui.h>
#include <pakfire/util.h>
-struct _PakfireTransaction {
- Pakfire pakfire;
- Transaction* transaction;
- PakfireStep* steps;
- size_t num_steps;
+struct pakfire_transaction {
+ struct pakfire* pakfire;
int nrefs;
+
+ Transaction* transaction;
+ char** userinstalled;
+
+ struct pakfire_archive** archives;
+ struct pakfire_package** packages;
+ size_t num;
+ size_t progress;
+
+ // Callbacks
+ struct pakfire_transaction_callbacks {
+ // Status
+ pakfire_status_callback status;
+ void* status_data;
+ } callbacks;
+};
+
+enum pakfire_actions {
+ PAKFIRE_ACTION_NOOP = 0,
+ PAKFIRE_ACTION_VERIFY,
+ PAKFIRE_ACTION_EXECUTE,
+ PAKFIRE_ACTION_PRETRANS,
+ PAKFIRE_ACTION_POSTTRANS,
+};
+
+enum pakfire_steps {
+ PAKFIRE_STEP_IGNORE = 0,
+ PAKFIRE_STEP_INSTALL,
+ PAKFIRE_STEP_REINSTALL,
+ PAKFIRE_STEP_ERASE,
+ PAKFIRE_STEP_UPGRADE,
+ PAKFIRE_STEP_DOWNGRADE,
+ PAKFIRE_STEP_OBSOLETE,
};
-static pakfire_step_type_t transaction_get_step_type(Transaction* transaction, Id id) {
- int type = transaction_type(transaction, id,
+static enum pakfire_steps pakfire_transaction_get_step_type(
+ struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
+ int type = transaction_type(transaction->transaction, pakfire_package_id(pkg),
SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_CHANGE_IS_REINSTALL);
// Translate solver types into our own types
}
}
-PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_create(Pakfire pakfire, Transaction* trans) {
- PakfireTransaction transaction = calloc(1, sizeof(*transaction));
- if (transaction) {
- DEBUG(pakfire, "Allocated Transaction at %p\n", transaction);
- transaction->nrefs = 1;
+static void pakfire_transaction_free_archives_and_packages(
+ struct pakfire_transaction* transaction) {
+ if (transaction->archives) {
+ for (unsigned int i = 0; i < transaction->num; i++)
+ if (transaction->archives[i])
+ pakfire_archive_unref(transaction->archives[i]);
+ free(transaction->archives);
- transaction->pakfire = pakfire_ref(pakfire);
+ transaction->archives = NULL;
+ }
- // Clone the transaction, so we get independent from what ever called this.
- if (trans) {
- transaction->transaction = transaction_create_clone(trans);
- transaction_order(transaction->transaction, 0);
- } else {
- transaction->transaction = transaction_create(trans->pool);
- }
+ if (transaction->packages) {
+ for (unsigned int i = 0; i < transaction->num; i++)
+ if (transaction->packages[i])
+ pakfire_package_unref(transaction->packages[i]);
+ free(transaction->packages);
- // Save total number of steps
- transaction->num_steps = transaction->transaction->steps.count;
+ transaction->packages = NULL;
+ }
+}
+
+static void pakfire_transaction_free(struct pakfire_transaction* transaction) {
+ pakfire_transaction_free_archives_and_packages(transaction);
+
+ if (transaction->userinstalled) {
+ for (char** userinstalled = transaction->userinstalled; *userinstalled; userinstalled++)
+ free(*userinstalled);
+ free(transaction->userinstalled);
+ }
+ transaction_free(transaction->transaction);
- // Import all steps
- PakfireStep* steps = transaction->steps = calloc(transaction->num_steps + 1, sizeof(*steps));
- for (unsigned int i = 0; i < transaction->num_steps; i++) {
- Id id = transaction->transaction->steps.elements[i];
+ pakfire_unref(transaction->pakfire);
+ free(transaction);
+}
- // Get the type
- pakfire_step_type_t type = transaction_get_step_type(transaction->transaction, id);
+static int pakfire_transaction_import_transaction(
+ struct pakfire_transaction* transaction, Solver* solver) {
+ // Clone the transaction to keep a copy of it
+ Transaction* t = solver_create_transaction(solver);
+ if (!t)
+ return 1;
- // Fetch the package
- PakfirePackage pkg = pakfire_package_create(pakfire, id);
+ transaction->transaction = t;
- // Append a new step
- *steps++ = pakfire_step_create(transaction, type, pkg);
+ // Order the transaction
+ transaction_order(t, 0);
- pakfire_package_unref(pkg);
- }
+ // Free any previous content
+ pakfire_transaction_free_archives_and_packages(transaction);
+
+ // How many steps?
+ transaction->num = t->steps.count;
+
+ // Allocate space for packages
+ transaction->packages = calloc(transaction->num, sizeof(*transaction->packages));
+ if (!transaction->packages)
+ return 1;
+
+ // Allocate space for archives
+ transaction->archives = calloc(transaction->num, sizeof(*transaction->archives));
+ if (!transaction->archives)
+ return 1;
+
+ // Create all packages
+ for (unsigned int i = 0; i < transaction->num; i++) {
+ transaction->packages[i] = pakfire_package_create_from_solvable(
+ transaction->pakfire, t->steps.elements[i]);
}
- return transaction;
+ return 0;
}
-PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_ref(PakfireTransaction transaction) {
- if (!transaction)
- return NULL;
+static int pakfire_transaction_import_userinstalled(
+ struct pakfire_transaction* t, Solver* solver) {
+ if (t->userinstalled) {
+ free(t->userinstalled);
+ t->userinstalled = NULL;
+ }
- transaction->nrefs++;
- return transaction;
+ Queue userinstalled;
+ queue_init(&userinstalled);
+
+ // Fetch a list of all packages that are installed by the user
+ solver_get_userinstalled(solver, &userinstalled, GET_USERINSTALLED_NAMES);
+
+ // Skip everything if the queue is empty
+ if (!userinstalled.count)
+ return 0;
+
+ t->userinstalled = calloc(userinstalled.count + 1, sizeof(*t->userinstalled));
+ if (!t->userinstalled) {
+ ERROR(t->pakfire, "Could not allocate userinstalled\n");
+ return 1;
+ }
+
+ Pool* pool = pakfire_get_solv_pool(t->pakfire);
+
+ // Store the names of all userinstalled packages
+ for (int i = 0; i < userinstalled.count; i++) {
+ const char* package = pool_id2str(pool, userinstalled.elements[i]);
+ t->userinstalled[i] = strdup(package);
+ }
+
+ return 0;
}
-void pakfire_transaction_free(PakfireTransaction transaction) {
- DEBUG(transaction->pakfire, "Releasing Transaction at %p\n", transaction);
- pakfire_unref(transaction->pakfire);
+int pakfire_transaction_create(struct pakfire_transaction** transaction,
+ struct pakfire* pakfire, Solver* solver) {
+ struct pakfire_transaction* t = calloc(1, sizeof(*t));
+ if (!t)
+ return ENOMEM;
- // Release all steps
- while (*transaction->steps)
- pakfire_step_unref(*transaction->steps++);
+ // Store reference to Pakfire
+ t->pakfire = pakfire_ref(pakfire);
- transaction_free(transaction->transaction);
- free(transaction);
+ // Initialize the reference counter
+ t->nrefs = 1;
+
+ // Import transaction
+ int r = pakfire_transaction_import_transaction(t, solver);
+ if (r)
+ goto ERROR;
+
+ // Import userinstalled
+ r = pakfire_transaction_import_userinstalled(t, solver);
+ if (r)
+ goto ERROR;
+
+ *transaction = t;
+ return 0;
+
+ERROR:
+ pakfire_transaction_free(t);
+ return 1;
}
-PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_unref(PakfireTransaction transaction) {
- if (!transaction)
- return NULL;
+PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_ref(
+ struct pakfire_transaction* transaction) {
+ transaction->nrefs++;
+ return transaction;
+}
+
+PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_unref(
+ struct pakfire_transaction* transaction) {
if (--transaction->nrefs > 0)
return transaction;
return NULL;
}
-PAKFIRE_EXPORT Pakfire pakfire_transaction_get_pakfire(PakfireTransaction transaction) {
- return pakfire_ref(transaction->pakfire);
+void pakfire_transaction_set_status_callback(struct pakfire_transaction* transaction,
+ pakfire_status_callback callback, void* data) {
+ transaction->callbacks.status = callback;
+ transaction->callbacks.status_data = data;
}
-Transaction* pakfire_transaction_get_transaction(PakfireTransaction transaction) {
- return transaction->transaction;
+static int pakfire_transaction_get_progress(struct pakfire_transaction* transaction) {
+ return transaction->progress * 100 / transaction->num;
}
-PAKFIRE_EXPORT size_t pakfire_transaction_count(PakfireTransaction transaction) {
- return transaction->num_steps;
+static void pakfire_transaction_status(struct pakfire_transaction* transaction,
+ const char* message, ...) {
+ char* buffer = NULL;
+ va_list args;
+ int r;
+
+ // Do nothing if callback isn't set
+ if (!transaction->callbacks.status)
+ return;
+
+ // Format the message
+ if (message) {
+ va_start(args, message);
+ r = vasprintf(&buffer, message, args);
+ va_end(args);
+
+ if (r < 0)
+ return;
+ }
+
+ // Fetch progress
+ const int progress = pakfire_transaction_get_progress(transaction);
+
+ // Call the callback
+ transaction->callbacks.status(transaction->pakfire,
+ transaction->callbacks.status_data, progress, buffer);
+
+ // Cleanup
+ if (buffer)
+ free(buffer);
}
-PAKFIRE_EXPORT ssize_t pakfire_transaction_installsizechange(PakfireTransaction transaction) {
+PAKFIRE_EXPORT size_t pakfire_transaction_count(struct pakfire_transaction* transaction) {
+ return transaction->num;
+}
+
+static ssize_t pakfire_transaction_installsizechange(struct pakfire_transaction* transaction) {
ssize_t sizechange = transaction_calc_installsizechange(transaction->transaction);
// Convert from kbytes to bytes
return sizechange * 1024;
}
-PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(PakfireTransaction transaction) {
+static ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
ssize_t size = 0;
- PakfireStep* steps = transaction->steps;
- while (*steps) {
- PakfireStep step = *steps++;
-
- size += pakfire_step_get_downloadsize(step);
- }
+ for (unsigned int i = 0; i < transaction->num; i++)
+ size += pakfire_package_get_downloadsize(transaction->packages[i]);
return size;
}
-PAKFIRE_EXPORT PakfireStep pakfire_transaction_get_step(PakfireTransaction transaction, unsigned int index) {
- PakfireStep* steps = transaction->steps;
-
- while (index-- && *steps)
- steps++;
-
- if (*steps)
- return pakfire_step_ref(*steps);
+static void pakfire_transaction_append_line(char*** lines, const char* format, ...) {
+ if (!lines)
+ return;
- return NULL;
-}
-
-PAKFIRE_EXPORT PakfirePackageList pakfire_transaction_get_packages(PakfireTransaction transaction, pakfire_step_type_t type) {
- PakfirePackageList packagelist = pakfire_packagelist_create(transaction->pakfire);
+ char* buffer = NULL;
+ va_list args;
+ int r;
- PakfireStep* steps = transaction->steps;
- while (*steps) {
- PakfireStep step = *steps++;
+ va_start(args, format);
+ r = vasprintf(&buffer, format, args);
+ va_end(args);
- if (pakfire_step_get_type(step) == type) {
- PakfirePackage package = pakfire_step_get_package(step);
- pakfire_packagelist_push(packagelist, package);
+ if (r < 0)
+ return;
- pakfire_package_unref(package);
- }
+ // Count lines
+ unsigned int count = 0;
+ if (*lines) {
+ for (char** l = *lines; *l; l++)
+ count++;
}
- // Sort list in place
- pakfire_packagelist_sort(packagelist);
+ // Increase size of array
+ *lines = reallocarray(*lines, count + 2, sizeof(**lines));
+ if (!*lines)
+ return;
- return packagelist;
+ // Append line and terminate lines
+ (*lines)[count] = buffer;
+ (*lines)[count + 1] = NULL;
}
-static void pakfire_transaction_add_headline(char** str, size_t width, const char* headline) {
- assert(headline);
-
- asprintf(str, "%s%s\n", *str, headline);
+static void pakfire_transaction_add_headline(char*** lines, size_t width, const char* headline) {
+ pakfire_transaction_append_line(lines, "%s\n", headline);
}
-static void pakfire_transaction_add_newline(char** str, size_t width) {
- asprintf(str, "%s\n", *str);
+static void pakfire_transaction_add_newline(char*** lines, size_t width) {
+ pakfire_transaction_append_line(lines, "\n");
}
-static void pakfire_transaction_add_line(char** str, size_t width, const char* name,
+static void pakfire_transaction_add_line(char*** lines, size_t width, const char* name,
const char* arch, const char* version, const char* repo, const char* size) {
- // XXX need to adapt to size
- asprintf(str, "%s %-21s %-8s %-21s %-18s %6s \n", *str, name, arch, version, repo, size);
+ pakfire_transaction_append_line(lines, " %-21s %-8s %-21s %-18s %6s \n",
+ name, arch, version, repo, size);
}
-static void pakfire_transaction_add_package(char** str, size_t width, PakfirePackage pkg) {
- PakfireRepo repo = pakfire_package_get_repo(pkg);
+static void pakfire_transaction_add_package(char*** lines, size_t width, struct pakfire_package* pkg) {
+ char size[128];
+
+ struct pakfire_repo* repo = pakfire_package_get_repo(pkg);
- unsigned long long size = pakfire_package_get_size(pkg);
- char* size_str = pakfire_format_size(size);
+ // Format size
+ int r = pakfire_format_size(size, pakfire_package_get_size(pkg));
+ if (r < 0)
+ return;
- pakfire_transaction_add_line(str, width,
+ pakfire_transaction_add_line(lines, width,
pakfire_package_get_name(pkg),
pakfire_package_get_arch(pkg),
pakfire_package_get_evr(pkg),
pakfire_repo_get_name(repo),
- size_str
+ size
);
pakfire_repo_unref(repo);
- free(size_str);
}
-static void pakfire_transaction_add_separator(char** str, size_t width) {
- while (width-- > 0)
- asprintf(str, "%s=", *str);
+static void pakfire_transaction_add_package_change(char*** lines, size_t width,
+ struct pakfire_package* old_pkg, struct pakfire_package* new_pkg) {
+ // Print the new package first
+ pakfire_transaction_add_package(lines, width, new_pkg);
- // newline
- asprintf(str, "%s\n", *str);
+ pakfire_transaction_append_line(lines,
+ " --> %s\n", pakfire_package_get_nevra(old_pkg));
}
-static size_t pakfire_transaction_add_section(char** str, size_t width, PakfireTransaction transaction,
- const char* headline, pakfire_step_type_t type) {
- PakfirePackageList list = pakfire_transaction_get_packages(transaction, type);
+static void pakfire_transaction_add_separator(char*** lines, size_t width) {
+ char* separator = alloca(width + 1);
- // Nothing to do if there are no packages in this stage
- size_t c = pakfire_packagelist_count(list);
- if (c == 0)
- goto END;
+ for (unsigned int i = 0; i < width; i++)
+ separator[i] = '=';
+ separator[width] = '\0';
- // Headline
- pakfire_transaction_add_headline(str, width, headline);
-
- // List each package
- for (unsigned int i = 0; i < c; i++) {
- PakfirePackage pkg = pakfire_packagelist_get(list, i);
- pakfire_transaction_add_package(str, width, pkg);
- pakfire_package_unref(pkg);
- }
+ pakfire_transaction_append_line(lines, "%s\n", separator);
+}
- // newline
- pakfire_transaction_add_newline(str, width);
+static void pakfire_transaction_add_usage_line(char*** lines, size_t width,
+ const char* headline, ssize_t size) {
+ char buffer[128];
-END:
- pakfire_packagelist_unref(list);
+ int r = pakfire_format_size(buffer, size);
+ if (r < 0)
+ return;
- return c;
+ pakfire_transaction_append_line(lines, "%-21s: %s\n", headline, buffer);
}
-static void pakfire_transaction_add_summary_line(char** str, size_t width, const char* headline, size_t pkgs) {
- if (pkgs > 0)
- asprintf(str, "%s%-20s %-4zu %s\n", *str, headline, pkgs, _("package(s)"));
-}
+static char* pakfire_transaction_join_lines(char** lines) {
+ if (!lines)
+ return NULL;
+
+ size_t size = 0;
+
+ // Determine total length
+ for (char** line = lines; *line; line++)
+ size += strlen(*line);
-static void pakfire_transaction_add_usage_line(char** str, size_t width, const char* headline, ssize_t size) {
- char* s = pakfire_format_size(size);
+ // Allocate memory large enough to hold the result
+ char* s = calloc(1, size + 1);
+ if (!s)
+ return s;
- asprintf(str, "%s%-21s: %s\n", *str, headline, s);
+ char* p = s;
+
+ for (char** line = lines; *line; line++) {
+ p += snprintf(p, s - p - 1, "%s", *line);
+ }
- free(s);
+ return s;
}
-PAKFIRE_EXPORT char* pakfire_transaction_dump(PakfireTransaction transaction, size_t width) {
- char* string = "";
+PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transaction, size_t width) {
+ char headline[1024];
+
+ Pool* pool = transaction->transaction->pool;
+ const int mode =
+ SOLVER_TRANSACTION_SHOW_OBSOLETES |
+ SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
+
+ char** lines = NULL;
// Header
- pakfire_transaction_add_separator(&string, width);
- pakfire_transaction_add_line(&string, width,
+ pakfire_transaction_add_separator(&lines, width);
+ pakfire_transaction_add_line(&lines, width,
_("Package"),
_("Arch"),
_("Version"),
_("Repository"),
_("Size")
);
- pakfire_transaction_add_separator(&string, width);
-
- // Show what we are doing
- size_t installing = pakfire_transaction_add_section(&string, width, transaction,
- _("Installing:"), PAKFIRE_STEP_INSTALL);
- size_t reinstalling = pakfire_transaction_add_section(&string, width, transaction,
- _("Reinstalling:"), PAKFIRE_STEP_REINSTALL);
- size_t updating = pakfire_transaction_add_section(&string, width, transaction,
- _("Updating:"), PAKFIRE_STEP_UPGRADE);
- size_t downgrading = pakfire_transaction_add_section(&string, width, transaction,
- _("Downgrading:"), PAKFIRE_STEP_DOWNGRADE);
- size_t removing = pakfire_transaction_add_section(&string, width, transaction,
- _("Removing:"), PAKFIRE_STEP_ERASE);
- size_t obsoleting = pakfire_transaction_add_section(&string, width, transaction,
- _("Obsoleting:"), PAKFIRE_STEP_OBSOLETE);
+ pakfire_transaction_add_separator(&lines, width);
+
+ Queue classes;
+ queue_init(&classes);
+
+ // Get all classes
+ transaction_classify(transaction->transaction, mode, &classes);
+
+ Queue pkgs;
+ queue_init(&pkgs);
+
+ /*
+ The classes queue now contains a list of all classes as a tuple of:
+ * The class type
+ * The number of packages in this class
+ * The from ID (for arch/vendor change)
+ * The to ID (for arch/vendor change)
+ */
+ for (int i = 0; i < classes.count; i += 4) {
+ Id class = classes.elements[i];
+ unsigned int count = classes.elements[i+1];
+
+ const char* from = pool_id2str(pool, classes.elements[i+2]);
+ const char* to = pool_id2str(pool, classes.elements[i+3]);
+
+ switch (class) {
+ case SOLVER_TRANSACTION_INSTALL:
+ if (count)
+ pakfire_string_format(headline, _("Installing %u packages:"), count);
+ else
+ pakfire_string_set(headline, _("Installing one package:"));
+ break;
+
+ case SOLVER_TRANSACTION_REINSTALLED:
+ if (count)
+ pakfire_string_format(headline, _("Reinstalling %u packages:"), count);
+ else
+ pakfire_string_set(headline, _("Reinstalling one package:"));
+ break;
+
+ case SOLVER_TRANSACTION_ERASE:
+ if (count)
+ pakfire_string_format(headline, _("Removing %u packages:"), count);
+ else
+ pakfire_string_set(headline, _("Removing one package:"));
+ break;
+
+ case SOLVER_TRANSACTION_UPGRADED:
+ if (count)
+ pakfire_string_format(headline, _("Updating %u packages:"), count);
+ else
+ pakfire_string_set(headline, _("Updating one package:"));
+ break;
+
+ case SOLVER_TRANSACTION_DOWNGRADED:
+ if (count)
+ pakfire_string_format(headline, _("Downgrading %u packages:"), count);
+ else
+ pakfire_string_set(headline, _("Downgrading one package:"));
+ break;
+
+ case SOLVER_TRANSACTION_CHANGED:
+ if (count)
+ pakfire_string_format(headline, _("Changing %u packages:"), count);
+ else
+ pakfire_string_set(headline, _("Changing one package:"));
+ break;
+
+ case SOLVER_TRANSACTION_ARCHCHANGE:
+ if (count)
+ pakfire_string_format(headline,
+ _("%u architecture changes from '%s' to '%s':"), count, from, to);
+ else
+ pakfire_string_format(headline,
+ _("One architecture change from '%s' to '%s':"), from, to);
+ break;
+
+ case SOLVER_TRANSACTION_VENDORCHANGE:
+ if (count)
+ pakfire_string_format(headline,
+ _("%u vendor changes from '%s' to '%s':"), count, from, to);
+ else
+ pakfire_string_format(headline,
+ _("One vendor change from '%s' to '%s':"), from, to);
+ break;
+
+ case SOLVER_TRANSACTION_IGNORE:
+ continue;
+ }
- // Summary
- pakfire_transaction_add_headline(&string, width, _("Transaction Summary"));
- pakfire_transaction_add_separator(&string, width);
+ // Show what we are doing
+ pakfire_transaction_add_headline(&lines, width, headline);
+
+ // Fetch packages in this class
+ transaction_classify_pkgs(transaction->transaction, mode, class,
+ classes.elements[i+2], classes.elements[i+3], &pkgs);
- pakfire_transaction_add_summary_line(&string, width, _("Installing:"), installing);
- pakfire_transaction_add_summary_line(&string, width, _("Reinstalling:"), reinstalling);
- pakfire_transaction_add_summary_line(&string, width, _("Updating:"), updating);
- pakfire_transaction_add_summary_line(&string, width, _("Downgrading:"), downgrading);
- pakfire_transaction_add_summary_line(&string, width, _("Removing:"), removing);
- pakfire_transaction_add_summary_line(&string, width, _("Obsoleting:"), obsoleting);
+ // List all packages
+ for (int j = 0; j < pkgs.count; j++) {
+ struct pakfire_package* old_pkg = pakfire_package_create_from_solvable(
+ transaction->pakfire, pkgs.elements[j]);
+ struct pakfire_package* new_pkg = NULL;
+
+ switch (class) {
+ case SOLVER_TRANSACTION_UPGRADED:
+ case SOLVER_TRANSACTION_DOWNGRADED:
+ new_pkg = pakfire_package_create_from_solvable(transaction->pakfire,
+ transaction_obs_pkg(transaction->transaction, pkgs.elements[j]));
+
+ pakfire_transaction_add_package_change(&lines, width, old_pkg, new_pkg);
+ break;
+
+ default:
+ pakfire_transaction_add_package(&lines, width, old_pkg);
+ break;
+ }
+
+ pakfire_package_unref(old_pkg);
+ if (new_pkg)
+ pakfire_package_unref(new_pkg);
+ }
+
+ // Newline
+ pakfire_transaction_add_newline(&lines, width);
+ }
+
+ queue_free(&classes);
+ queue_free(&pkgs);
+
+ // Summary
+ pakfire_transaction_add_headline(&lines, width, _("Transaction Summary"));
+ pakfire_transaction_add_separator(&lines, width);
// How much do we need to download?
size_t downloadsize = pakfire_transaction_downloadsize(transaction);
+
if (downloadsize > 0)
- pakfire_transaction_add_usage_line(&string, width,
+ pakfire_transaction_add_usage_line(&lines, width,
_("Total Download Size"), downloadsize);
// How much more space do we need?
ssize_t sizechange = pakfire_transaction_installsizechange(transaction);
-
- pakfire_transaction_add_usage_line(&string, width,
+ pakfire_transaction_add_usage_line(&lines, width,
(sizechange >= 0) ? _("Installed Size") : _("Freed Size"), sizechange);
- // Remove trailing newline
- size_t l = strlen(string) - 1;
-
- if (l > 0 && string[l] == '\n')
- string[l] = '\0';
+ // Join all lines together
+ char* string = pakfire_transaction_join_lines(lines);
DEBUG(transaction->pakfire, "Transaction: %s\n", string);
+ // Free lines
+ if (lines) {
+ for (char** line = lines; *line; line++)
+ free(*line);
+ free(lines);
+ }
+
return string;
}
-static int pakfire_transaction_run_steps(PakfireTransaction transaction,
- struct pakfire_db* db, const pakfire_action_type_t action) {
+static void* pakfire_transaction_fileconflict_callback(
+ Pool* pool, Id p, void* data) {
+ return NULL;
+}
+
+static int pakfire_transaction_check_fileconflicts(
+ struct pakfire_transaction* transaction) {
+ Pool* pool = pakfire_get_solv_pool(transaction->pakfire);
+
+ const int flags =
+ FINDFILECONFLICTS_USE_SOLVABLEFILELIST |
+ FINDFILECONFLICTS_CHECK_DIRALIASING |
+ FINDFILECONFLICTS_USE_ROOTDIR;
+
+ Queue pkgs;
+ queue_init(&pkgs);
+
+ Queue conflicts;
+ queue_init(&conflicts);
+
+ // Fetch all installed packages
+ int newpkgs = transaction_installedresult(transaction->transaction, &pkgs);
+
+ // Perform check
+ int count = pool_findfileconflicts(pool, &pkgs, newpkgs, &conflicts, flags,
+ pakfire_transaction_fileconflict_callback, NULL);
+
+ DEBUG(transaction->pakfire, "Found %d file conflict(s)\n", count);
+
+ // Free everything
+ queue_free(&pkgs);
+ queue_free(&conflicts);
+
+ return (count > 0);
+}
+
+static int pakfire_transaction_check(struct pakfire_transaction* transaction) {
+ int r;
+
+ // Check for any file conflicts
+ r = pakfire_transaction_check_fileconflicts(transaction);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int pakfire_transaction_verify(struct pakfire_transaction* transaction,
+ struct pakfire_package* pkg, struct pakfire_archive* archive) {
+ int r;
+
+ const char* nevra = pakfire_package_get_nevra(pkg);
+
+ // Nothing to do if this step does not have an archive
+ if (!archive) {
+ DEBUG(transaction->pakfire, "Package %s requires no archive\n", nevra);
+ return 0;
+ }
+
+ enum pakfire_digest_types digest_type = 0;
+
+ // Fetch digest from package
+ const unsigned char* expected_digest = pakfire_package_get_digest(pkg, &digest_type);
+ if (!expected_digest) {
+ DEBUG(transaction->pakfire, "Package %s has no digest\n", nevra);
+ return 0;
+ }
+
+ unsigned char computed_digest[EVP_MAX_MD_SIZE];
+ size_t digest_length = 0;
+
+ // Compute digest of the archive
+ r = pakfire_archive_digest(archive, digest_type, computed_digest, &digest_length);
+ if (r) {
+ ERROR(transaction->pakfire, "Could not compute digest for %s: %m\n", nevra);
+ return r;
+ }
+
+ // Compare digests
+ r = CRYPTO_memcmp(computed_digest, expected_digest, digest_length);
+ if (r) {
+ ERROR(transaction->pakfire, "Digests of %s do not match\n", nevra);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int pakfire_transaction_run_script(struct pakfire_transaction* transaction,
+ struct pakfire_db* db, const char* type, struct pakfire_package* pkg, struct pakfire_archive* archive) {
+ struct pakfire_scriptlet* scriptlet = NULL;
+
+ // Fetch scriptlet from archive if possible
+ if (archive)
+ scriptlet = pakfire_archive_get_scriptlet(archive, type);
+ else
+ scriptlet = pakfire_db_get_scriptlet(db, pkg, type);
+
+ // Nothing to do if there are no scriptlets
+ if (!scriptlet)
+ return 0;
+
+ // Execute the scriptlet
+ pakfire_scriptlet_execute(scriptlet);
+
+ pakfire_scriptlet_unref(scriptlet);
+
+ return 0;
+}
+
+static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
+ struct pakfire_package* pkg, struct pakfire_archive* archive) {
+ const char* nevra = pakfire_package_get_nevra(pkg);
+
+ // Update status
+ pakfire_transaction_status(transaction, _("Installing %s..."), nevra);
+
+ // Extract payload
+ int r = pakfire_archive_extract(archive);
+ if (r) {
+ ERROR(transaction->pakfire, "Could not extract package %s: %m\n",
+ nevra);
+ return r;
+ }
+
+ // Is it necessary to call ldconfig?
+ struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive);
+ if (filelist) {
+ int need_ldconfig = pakfire_filelist_contains(filelist, "*/lib*.so.?");
+
+ // Update the runtime linker cache
+ if (need_ldconfig)
+ pakfire_jail_ldconfig(transaction->pakfire);
+
+ pakfire_filelist_unref(filelist);
+ }
+
+ return r;
+}
+
+static int pakfire_transaction_erase(struct pakfire_transaction* transaction,
+ struct pakfire_db* db, struct pakfire_package* pkg) {
+ struct pakfire_filelist* filelist = NULL;
+ int r;
+
+ // Fetch filelist
+ r = pakfire_db_package_filelist(db, &filelist, pkg);
+ if (r)
+ goto ERROR;
+
+ const size_t length = pakfire_filelist_size(filelist);
+
+ // Delete all files
+ for (unsigned int i = 0; i < length; i++) {
+ struct pakfire_file* file = pakfire_filelist_get(filelist, i);
+
+ // Remove the file
+ r = pakfire_file_remove(file);
+ pakfire_file_unref(file);
+
+ // Break on any errors
+ if (r)
+ goto ERROR;
+ }
+
+ // Update the runtime linker cache after all files have been removed
+ pakfire_jail_ldconfig(transaction->pakfire);
+
+ERROR:
+ if (filelist)
+ pakfire_filelist_unref(filelist);
+
+ return r;
+}
+
+static const char* pakfire_action_type_string(enum pakfire_actions type) {
+ switch (type) {
+ case PAKFIRE_ACTION_NOOP:
+ return "NOOP";
+
+ case PAKFIRE_ACTION_VERIFY:
+ return "VERIFY";
+
+ case PAKFIRE_ACTION_EXECUTE:
+ return "EXECUTE";
+
+ case PAKFIRE_ACTION_PRETRANS:
+ return "PRETRANS";
+
+ case PAKFIRE_ACTION_POSTTRANS:
+ return "POSTTRANS";
+ }
+
+ return NULL;
+}
+
+static int pakfire_transaction_package_is_userinstalled(
+ struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
+ // No packages on the list
+ if (!transaction->userinstalled)
+ return 0;
+
+ const char* name = pakfire_package_get_name(pkg);
+
+ // Check if the package is on the list
+ for (char** elem = transaction->userinstalled; *elem; elem++) {
+ if (strcmp(name, *elem) == 0)
+ return 1;
+ }
+
+ // Not found
+ return 0;
+}
+
+static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
+ struct pakfire_db* db, const enum pakfire_actions action, struct pakfire_package* pkg, struct pakfire_archive* archive) {
+ if (!pkg) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ DEBUG(transaction->pakfire, "Running %s for %s\n",
+ pakfire_action_type_string(action), pakfire_package_get_nevra(pkg));
+
+ enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
+
int r = 0;
+ switch (action) {
+ // Verify this step
+ case PAKFIRE_ACTION_VERIFY:
+ r = pakfire_transaction_verify(transaction, pkg, archive);
+ break;
- // Walk through all steps
- PakfireStep* steps = transaction->steps;
- while (*steps) {
- PakfireStep step = *steps++;
+ // Run the pre-transaction scripts
+ case PAKFIRE_ACTION_PRETRANS:
+ switch (type) {
+ case PAKFIRE_STEP_INSTALL:
+ case PAKFIRE_STEP_REINSTALL:
+ r = pakfire_transaction_run_script(transaction, db,
+ "pretransin", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_UPGRADE:
+ case PAKFIRE_STEP_DOWNGRADE:
+ r = pakfire_transaction_run_script(transaction, db,
+ "pretransup", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_ERASE:
+ case PAKFIRE_STEP_OBSOLETE:
+ r = pakfire_transaction_run_script(transaction, db,
+ "pretransun", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_IGNORE:
+ break;
+ }
+ break;
+
+ // Run the post-transaction scripts
+ case PAKFIRE_ACTION_POSTTRANS:
+ switch (type) {
+ case PAKFIRE_STEP_INSTALL:
+ case PAKFIRE_STEP_REINSTALL:
+ r = pakfire_transaction_run_script(transaction, db,
+ "posttransin", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_UPGRADE:
+ case PAKFIRE_STEP_DOWNGRADE:
+ r = pakfire_transaction_run_script(transaction, db,
+ "posttransup", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_ERASE:
+ case PAKFIRE_STEP_OBSOLETE:
+ r = pakfire_transaction_run_script(transaction, db,
+ "posttransun", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_IGNORE:
+ break;
+ }
+ break;
+
+ // Execute the action of this script
+ case PAKFIRE_ACTION_EXECUTE:
+ // Increment progress
+ transaction->progress++;
+
+ // Update progress callback
+ pakfire_transaction_status(transaction, NULL);
+
+ switch (type) {
+ case PAKFIRE_STEP_INSTALL:
+ case PAKFIRE_STEP_REINSTALL:
+ r = pakfire_transaction_run_script(transaction, db,
+ "prein", pkg, archive);
+ if (r)
+ break;
+
+ r = pakfire_transaction_extract(transaction, pkg, archive);
+ if (r)
+ break;
+
+ // Remove package metadata first when reinstalling
+ if (type == PAKFIRE_STEP_REINSTALL) {
+ r = pakfire_db_remove_package(db, pkg);
+ if (r)
+ break;
+ }
+
+ r = pakfire_db_add_package(db, pkg, archive,
+ pakfire_transaction_package_is_userinstalled(transaction, pkg));
+ if (r)
+ break;
+
+ r = pakfire_transaction_run_script(transaction, db,
+ "postin", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_UPGRADE:
+ case PAKFIRE_STEP_DOWNGRADE:
+ r = pakfire_transaction_run_script(transaction, db,
+ "preup", pkg, archive);
+ if (r)
+ break;
+
+ r = pakfire_transaction_extract(transaction, pkg, archive);
+ if (r)
+ break;
+
+ r = pakfire_db_add_package(db, pkg, archive,
+ pakfire_transaction_package_is_userinstalled(transaction, pkg));
+ if (r)
+ break;
+
+ r = pakfire_transaction_run_script(transaction, db,
+ "postup", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_ERASE:
+ case PAKFIRE_STEP_OBSOLETE:
+ r = pakfire_transaction_run_script(transaction, db,
+ "preun", pkg, archive);
+ if (r)
+ break;
+
+ r = pakfire_transaction_erase(transaction, db, pkg);
+ if (r)
+ break;
+
+ r = pakfire_db_remove_package(db, pkg);
+ if (r)
+ break;
+
+ r = pakfire_transaction_run_script(transaction, db,
+ "postun", pkg, archive);
+ break;
+
+ case PAKFIRE_STEP_IGNORE:
+ break;
+ }
+ break;
+
+ // Do nothing
+ case PAKFIRE_ACTION_NOOP:
+ break;
+ }
- // Verify the step
- r = pakfire_step_run(step, db, action);
+ if (r)
+ ERROR(transaction->pakfire, "Step has failed: %s\n", strerror(r));
+
+ return r;
+}
+
+static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction,
+ struct pakfire_db* db, enum pakfire_actions action) {
+ int r = 0;
+
+ // Update status
+ switch (action) {
+ case PAKFIRE_ACTION_VERIFY:
+ pakfire_transaction_status(transaction, _("Verifying packages..."));
+ break;
+
+ case PAKFIRE_ACTION_PRETRANS:
+ pakfire_transaction_status(transaction, _("Preparing installation..."));
+ break;
+
+ case PAKFIRE_ACTION_POSTTRANS:
+ pakfire_transaction_status(transaction, _("Finishing up..."));
+ break;
+
+ default:
+ break;
+ }
+
+ // Walk through all steps
+ for (unsigned int i = 0; i < transaction->num; i++) {
+ r = pakfire_transaction_run_step(transaction, db, action,
+ transaction->packages[i], transaction->archives[i]);
// End loop if action was unsuccessful
if (r) {
- DEBUG(transaction->pakfire, "Step %p failed with code %d\n", step, r);
+ DEBUG(transaction->pakfire, "Step %d failed: %m\n", i);
break;
}
}
return r;
}
-PAKFIRE_EXPORT int pakfire_transaction_run(PakfireTransaction transaction) {
+static int pakfire_transaction_open_archives(struct pakfire_transaction* transaction) {
+ for (unsigned int i = 0; i < transaction->num; i++) {
+ struct pakfire_package* pkg = transaction->packages[i];
+
+ // Fetch the type
+ enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
+
+ // Do we need the archive?
+ switch (type) {
+ case PAKFIRE_STEP_INSTALL:
+ case PAKFIRE_STEP_REINSTALL:
+ case PAKFIRE_STEP_UPGRADE:
+ case PAKFIRE_STEP_DOWNGRADE:
+ case PAKFIRE_STEP_OBSOLETE:
+ break;
+
+ case PAKFIRE_STEP_ERASE:
+ case PAKFIRE_STEP_IGNORE:
+ continue;
+ }
+
+ transaction->archives[i] = pakfire_package_get_archive(pkg);
+ if (!transaction->archives[i]) {
+ ERROR(transaction->pakfire, "Could not open archive for %s: %m\n",
+ pakfire_package_get_nevra(pkg));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int pakfire_transaction_perform(struct pakfire_transaction* transaction) {
+ struct pakfire_repo* repo = NULL;
struct pakfire_db* db;
int r;
DEBUG(transaction->pakfire, "Running Transaction %p\n", transaction);
+ // Open all archives
+ r = pakfire_transaction_open_archives(transaction);
+ if (r)
+ return r;
+
// Open the database
r = pakfire_db_open(&db, transaction->pakfire, PAKFIRE_DB_READWRITE);
if (r) {
DEBUG(transaction->pakfire, "The transaction has finished successfully\n");
+ // Reload database for next transaction
+
+ repo = pakfire_get_installed_repo(transaction->pakfire);
+ if (!repo)
+ goto ERROR;
+
+ // Reload the database
+ r = pakfire_db_load(db, repo);
+
ERROR:
- // Free the database
+ if (repo)
+ pakfire_repo_unref(repo);
pakfire_db_unref(db);
return r;
}
+
+static int pakfire_transaction_download_package(struct pakfire_transaction* transaction,
+ struct pakfire_downloader* downloader, struct pakfire_package* pkg) {
+ int r = 1;
+ struct pakfire_repo* repo = NULL;
+ struct pakfire_mirrorlist* mirrorlist = NULL;
+
+ // Fetch the repository to download from
+ repo = pakfire_package_get_repo(pkg);
+ if (!repo)
+ goto ERROR;
+
+ // Fetch baseurl
+ const char* baseurl = pakfire_repo_get_baseurl(repo);
+
+ // Fetch mirrorlist
+ mirrorlist = pakfire_repo_get_mirrorlist(repo);
+
+ const char* nevra = pakfire_package_get_nevra(pkg);
+ if (!nevra)
+ goto ERROR;
+
+ // Where to store the package?
+ const char* path = pakfire_package_get_path(pkg);
+ if (!path) {
+ ERROR(transaction->pakfire, "Could not retrieve package path for %s: %m\n", nevra);
+ goto ERROR;
+ }
+
+ // What file to download?
+ const char* filename = pakfire_package_get_filename(pkg);
+ if (!filename) {
+ ERROR(transaction->pakfire, "Could not retrieve filename for package %s: %m\n", nevra);
+ goto ERROR;
+ }
+
+ enum pakfire_digest_types digest_type = 0;
+
+ // Retrieve package digest
+ const unsigned char* digest = pakfire_package_get_digest(pkg, &digest_type);
+ const size_t digest_length = pakfire_digest_length(digest_type);
+
+ // Add transfer to downloader
+ r = pakfire_downloader_add_transfer(downloader, baseurl, mirrorlist,
+ nevra, filename, path, digest_type, digest, digest_length, 0);
+
+ERROR:
+ if (mirrorlist)
+ pakfire_mirrorlist_unref(mirrorlist);
+ if (repo)
+ pakfire_repo_unref(repo);
+
+ return r;
+}
+
+static int pakfire_transaction_package_needs_download(
+ struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
+ enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
+ switch (type) {
+ case PAKFIRE_STEP_INSTALL:
+ case PAKFIRE_STEP_REINSTALL:
+ case PAKFIRE_STEP_DOWNGRADE:
+ case PAKFIRE_STEP_UPGRADE:
+ break;
+
+ // No need to download for these steps
+ default:
+ return 0;
+ }
+
+ // No download required if this package is already installed
+ if (pakfire_package_is_installed(pkg))
+ return 0;
+
+ const char* path = pakfire_package_get_path(pkg);
+
+ // Does the file exist?
+ int r = access(path, R_OK);
+ if (r == 0)
+ return 0;
+
+ // This package needs to be downloaded
+ return 1;
+}
+
+PAKFIRE_EXPORT int pakfire_transaction_download(struct pakfire_transaction* transaction) {
+ struct pakfire_downloader* downloader;
+ int r;
+
+ // Initialize the downloader
+ r = pakfire_downloader_create(&downloader, transaction->pakfire);
+ if (r) {
+ ERROR(transaction->pakfire, "Could not initialize downloader: %m\n");
+ return 1;
+ }
+
+ // Add all packages that need to be downloaded
+ for (unsigned int i = 0; i < transaction->num; i++) {
+ struct pakfire_package* pkg = transaction->packages[i];
+
+ if (!pakfire_transaction_package_needs_download(transaction, pkg))
+ continue;
+
+ // Enqueue download
+ r = pakfire_transaction_download_package(transaction, downloader, pkg);
+ if (r) {
+ const char* nevra = pakfire_package_get_nevra(pkg);
+
+ ERROR(transaction->pakfire, "Could not add download to queue: %s: %m\n", nevra);
+ goto ERROR;
+ }
+ }
+
+ // Run the downloader
+ r = pakfire_downloader_run(downloader);
+
+ERROR:
+ pakfire_downloader_unref(downloader);
+
+ return r;
+}
+
+PAKFIRE_EXPORT int pakfire_transaction_run(
+ struct pakfire_transaction* transaction, int flags) {
+ int r;
+
+ // Skip running an empty transaction
+ if (!transaction->num) {
+ DEBUG(transaction->pakfire, "Empty transaction. Skipping...\n");
+ return 0;
+ }
+
+ // Show what would be done
+ char* dump = pakfire_transaction_dump(transaction, 80);
+
+ // Check if we should continue
+ r = pakfire_confirm(transaction->pakfire, dump, _("Is this okay? [y/N]"));
+ if (r) {
+ ERROR(transaction->pakfire, "Transaction aborted upon user request\n");
+ goto ERROR;
+ }
+
+ // Write transaction dump to log
+ INFO(transaction->pakfire, "%s\n", dump);
+
+ // Perform a check if this can actually be run
+ r = pakfire_transaction_check(transaction);
+ if (r)
+ goto ERROR;
+
+ // End here for a dry run
+ if (flags & PAKFIRE_TRANSACTION_DRY_RUN)
+ goto ERROR;
+
+ // Download what we need
+ r = pakfire_transaction_download(transaction);
+ if (r)
+ goto ERROR;
+
+ // Perform all steps
+ r = pakfire_transaction_perform(transaction);
+ if (r)
+ goto ERROR;
+
+ERROR:
+ // Cleanup
+ free(dump);
+
+ return r;
+}