# #
#############################################################################*/
-#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>
-PakfireTransaction pakfire_transaction_create(PakfirePool pool, Transaction* trans) {
- PakfireTransaction transaction = pakfire_calloc(1, sizeof(*transaction));
- transaction->pool = pool;
+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 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
+ switch (type) {
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_MULTIINSTALL:
+ return PAKFIRE_STEP_INSTALL;
+
+ case SOLVER_TRANSACTION_REINSTALL:
+ case SOLVER_TRANSACTION_MULTIREINSTALL:
+ return PAKFIRE_STEP_REINSTALL;
+
+ case SOLVER_TRANSACTION_ERASE:
+ return PAKFIRE_STEP_ERASE;
+
+ case SOLVER_TRANSACTION_DOWNGRADE:
+ return PAKFIRE_STEP_DOWNGRADE;
+
+ case SOLVER_TRANSACTION_UPGRADE:
+ return PAKFIRE_STEP_UPGRADE;
+
+ case SOLVER_TRANSACTION_OBSOLETES:
+ return PAKFIRE_STEP_OBSOLETE;
+
+ // Anything we don't care about
+ case SOLVER_TRANSACTION_IGNORE:
+ case SOLVER_TRANSACTION_REINSTALLED:
+ case SOLVER_TRANSACTION_DOWNGRADED:
+ default:
+ return PAKFIRE_STEP_IGNORE;
+ }
+}
+
+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);
- // 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);
+ transaction->archives = NULL;
}
- return transaction;
+ 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);
+
+ transaction->packages = NULL;
+ }
}
-void pakfire_transaction_free(PakfireTransaction transaction) {
+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);
- pakfire_free(transaction);
-}
-size_t pakfire_transaction_count(PakfireTransaction transaction) {
- return transaction->transaction->steps.count;
+ pakfire_unref(transaction->pakfire);
+ free(transaction);
}
-ssize_t pakfire_transaction_installsizechange(PakfireTransaction transaction) {
- ssize_t sizechange = transaction_calc_installsizechange(transaction->transaction);
+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;
- // Convert from kbytes to bytes
- return sizechange * 1024;
+ transaction->transaction = t;
+
+ // Order the transaction
+ transaction_order(t, 0);
+
+ // 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 0;
}
-ssize_t pakfire_transaction_downloadsize(PakfireTransaction transaction) {
- PakfirePool pool = pakfire_transaction_pool(transaction);
- ssize_t size = 0;
+static int pakfire_transaction_import_userinstalled(
+ struct pakfire_transaction* t, Solver* solver) {
+ if (t->userinstalled) {
+ free(t->userinstalled);
+ t->userinstalled = NULL;
+ }
- for (int i = 0; i < transaction->transaction->steps.count; i++) {
- Id p = transaction->transaction->steps.elements[i];
- Id t = transaction_type(transaction->transaction, p,
- SOLVER_TRANSACTION_SHOW_OBSOLETES |
- SOLVER_TRANSACTION_CHANGE_IS_REINSTALL |
- SOLVER_TRANSACTION_SHOW_ALL |
- SOLVER_TRANSACTION_SHOW_ACTIVE);
+ Queue userinstalled;
+ queue_init(&userinstalled);
- // Erasing a package does not require us to download it
- if (t == SOLVER_TRANSACTION_ERASE)
- continue;
+ // Fetch a list of all packages that are installed by the user
+ solver_get_userinstalled(solver, &userinstalled, GET_USERINSTALLED_NAMES);
- // Get the package for this step
- PakfirePackage pkg = pakfire_package_create(pool, p);
+ // Skip everything if the queue is empty
+ if (!userinstalled.count)
+ return 0;
- if (!pakfire_package_is_cached(pkg))
- size += pakfire_package_get_downloadsize(pkg);
+ t->userinstalled = calloc(userinstalled.count + 1, sizeof(*t->userinstalled));
+ if (!t->userinstalled) {
+ ERROR(t->pakfire, "Could not allocate userinstalled\n");
+ return 1;
+ }
- pakfire_package_free(pkg);
+ 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 size;
+ return 0;
}
-PakfireStep pakfire_transaction_get_step(PakfireTransaction transaction, int index) {
- Transaction* trans = transaction->transaction;
+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;
- if (index >= trans->steps.count)
- return NULL;
+ // Store reference to Pakfire
+ t->pakfire = pakfire_ref(pakfire);
+
+ // 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;
- return pakfire_step_create(transaction, trans->steps.elements[index]);
+ERROR:
+ pakfire_transaction_free(t);
+ return 1;
}
-PakfirePackageList pakfire_transaction_get_packages(PakfireTransaction transaction, int type) {
- PakfirePool pool = pakfire_transaction_pool(transaction);
+PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_ref(
+ struct pakfire_transaction* transaction) {
+ transaction->nrefs++;
- PakfirePackageList packagelist = pakfire_packagelist_create();
+ return transaction;
+}
- for (int i = 0; i < transaction->transaction->steps.count; i++) {
- Id p = transaction->transaction->steps.elements[i];
- Id t = transaction_type(transaction->transaction, p,
- SOLVER_TRANSACTION_SHOW_OBSOLETES |
- SOLVER_TRANSACTION_CHANGE_IS_REINSTALL |
- SOLVER_TRANSACTION_SHOW_ALL |
- SOLVER_TRANSACTION_SHOW_ACTIVE);
+PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_unref(
+ struct pakfire_transaction* transaction) {
+ if (--transaction->nrefs > 0)
+ return transaction;
- if (t == type) {
- PakfirePackage package = pakfire_package_create(pool, p);
- pakfire_packagelist_push(packagelist, package);
- }
+ pakfire_transaction_free(transaction);
+ return NULL;
+}
+
+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;
+}
+
+static int pakfire_transaction_get_progress(struct pakfire_transaction* transaction) {
+ return transaction->progress * 100 / transaction->num;
+}
+
+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;
}
- // Sort list in place
- pakfire_packagelist_sort(packagelist);
+ // 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 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;
+}
+
+static ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
+ ssize_t size = 0;
+
+ for (unsigned int i = 0; i < transaction->num; i++)
+ size += pakfire_package_get_downloadsize(transaction->packages[i]);
- return packagelist;
+ return size;
}
-static void pakfire_transaction_add_headline(char** str, size_t width, const char* headline) {
- assert(headline);
+static void pakfire_transaction_append_line(char*** lines, const char* format, ...) {
+ if (!lines)
+ return;
+
+ char* buffer = NULL;
+ va_list args;
+ int r;
+
+ va_start(args, format);
+ r = vasprintf(&buffer, format, args);
+ va_end(args);
- asprintf(str, "%s%s\n", *str, headline);
+ if (r < 0)
+ return;
+
+ // Count lines
+ unsigned int count = 0;
+ if (*lines) {
+ for (char** l = *lines; *l; l++)
+ count++;
+ }
+
+ // Increase size of array
+ *lines = reallocarray(*lines, count + 2, sizeof(**lines));
+ if (!*lines)
+ return;
+
+ // Append line and terminate lines
+ (*lines)[count] = buffer;
+ (*lines)[count + 1] = NULL;
}
-static void pakfire_transaction_add_newline(char** str, size_t width) {
- asprintf(str, "%s\n", *str);
+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_line(char** str, size_t width, const char* name,
+static void pakfire_transaction_add_newline(char*** lines, size_t width) {
+ pakfire_transaction_append_line(lines, "\n");
+}
+
+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_free(repo);
- pakfire_free(size_str);
+ pakfire_repo_unref(repo);
}
-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, int 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_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_free(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;
- pakfire_free(s);
+ for (char** line = lines; *line; line++) {
+ p += snprintf(p, s - p - 1, "%s", *line);
+ }
+
+ return s;
}
-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:"), SOLVER_TRANSACTION_INSTALL);
- size_t reinstalling = pakfire_transaction_add_section(&string, width, transaction,
- _("Reinstalling:"), SOLVER_TRANSACTION_REINSTALL);
- size_t updating = pakfire_transaction_add_section(&string, width, transaction,
- _("Updating:"), SOLVER_TRANSACTION_UPGRADE);
- size_t downgrading = pakfire_transaction_add_section(&string, width, transaction,
- _("Downgrading:"), SOLVER_TRANSACTION_DOWNGRADE);
- size_t removing = pakfire_transaction_add_section(&string, width, transaction,
- _("Removing:"), SOLVER_TRANSACTION_ERASE);
- size_t obsoleting = pakfire_transaction_add_section(&string, width, transaction,
- _("Obsoleting:"), SOLVER_TRANSACTION_OBSOLETES);
+ 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);
+
+ // 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);
- 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);
+ // 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;
+ // Join all lines together
+ char* string = pakfire_transaction_join_lines(lines);
- if (l > 0 && string[l] == '\n')
- string[l] = '\0';
+ 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, const pakfire_action_type action) {
- size_t steps = pakfire_transaction_count(transaction);
+static void* pakfire_transaction_fileconflict_callback(
+ Pool* pool, Id p, void* data) {
+ return NULL;
+}
- // Walk through all steps
- int r = 0;
- for (unsigned int i = 0; i < steps; i++) {
- PakfireStep step = pakfire_transaction_get_step(transaction, i);
+static int pakfire_transaction_check_fileconflicts(
+ struct pakfire_transaction* transaction) {
+ Pool* pool = pakfire_get_solv_pool(transaction->pakfire);
- // Verify the step
- r = pakfire_step_run(step, action);
+ const int flags =
+ FINDFILECONFLICTS_USE_SOLVABLEFILELIST |
+ FINDFILECONFLICTS_CHECK_DIRALIASING |
+ FINDFILECONFLICTS_USE_ROOTDIR;
- // Free memory
- pakfire_step_free(step);
+ Queue pkgs;
+ queue_init(&pkgs);
- // End loop if action was unsuccessful
+ 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;
+
+ // 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;
}
+ if (r)
+ ERROR(transaction->pakfire, "Step has failed: %s\n", strerror(r));
+
return r;
}
-int pakfire_transaction_run(PakfireTransaction transaction) {
+static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction,
+ struct pakfire_db* db, enum pakfire_actions action) {
int r = 0;
- // Verify steps
- r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_VERIFY);
+ // 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 %d failed: %m\n", i);
+ break;
+ }
+ }
+
+ return r;
+}
+
+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) {
+ ERROR(transaction->pakfire, "Could not open the database\n");
+ return r;
+ }
+
+ // Verify steps
+ r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_VERIFY);
+ if (r)
+ goto ERROR;
+
// Execute all pre transaction actions
- r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_PRETRANS);
+ r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_PRETRANS);
if (r)
- return r;
+ goto ERROR;
- r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_EXECUTE);
+ r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_EXECUTE);
if (r)
- return r;
+ goto ERROR;
// Execute all post transaction actions
- r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_POSTTRANS);
+ r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_POSTTRANS);
if (r)
- return r;
+ goto ERROR;
- return 0;
+ 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:
+ 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;
}