#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/execute.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/pakfire.h>
#include <pakfire/private.h>
#include <pakfire/repo.h>
+#include <pakfire/string.h>
#include <pakfire/transaction.h>
#include <pakfire/ui.h>
#include <pakfire/util.h>
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 {
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;
+ }
+
+ // 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;
}
return sizechange * 1024;
}
-PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
+static ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
ssize_t size = 0;
for (unsigned int i = 0; i < transaction->num; i++)
struct pakfire_repo* repo = pakfire_package_get_repo(pkg);
// Format size
- pakfire_format_size(size, sizeof(size) - 1, pakfire_package_get_size(pkg));
+ int r = pakfire_format_size(size, pakfire_package_get_size(pkg));
+ if (r < 0)
+ return;
pakfire_transaction_add_line(lines, width,
pakfire_package_get_name(pkg),
static void pakfire_transaction_add_usage_line(char*** lines, size_t width,
const char* headline, ssize_t size) {
char buffer[128];
- pakfire_format_size(buffer, sizeof(buffer) - 1, size);
+
+ int r = pakfire_format_size(buffer, size);
+ if (r < 0)
+ return;
pakfire_transaction_append_line(lines, "%-21s: %s\n", headline, buffer);
}
return string;
}
+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)
+ 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;
+ }
- pakfire_archive_verify_status_t status;
+ unsigned char computed_digest[EVP_MAX_MD_SIZE];
+ size_t digest_length = 0;
- // Verify the archive
- int r = pakfire_archive_verify(archive, &status, NULL);
- if (r)
+ // 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;
+ }
- // This function will return a binary status which is zero for success and
- // anything else for errors, etc...
- switch (status) {
- // Good
- case PAKFIRE_ARCHIVE_VERIFY_OK:
- case PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED:
- return 0;
-
- // Bad
- default:
- break;
+ // 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 1;
+ return 0;
}
static int pakfire_transaction_run_script(struct pakfire_transaction* transaction,
static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
struct pakfire_package* pkg, struct pakfire_archive* archive) {
- // Extract payload to the root of the Pakfire instance
- int r = pakfire_archive_extract(archive, NULL);
+ 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",
- pakfire_package_get_nevra(pkg));
+ nevra);
return r;
}
// Update the runtime linker cache
if (need_ldconfig)
- pakfire_execute_ldconfig(transaction->pakfire);
+ pakfire_jail_ldconfig(transaction->pakfire);
pakfire_filelist_unref(filelist);
}
}
// Update the runtime linker cache after all files have been removed
- pakfire_execute_ldconfig(transaction->pakfire);
+ pakfire_jail_ldconfig(transaction->pakfire);
ERROR:
if (filelist)
// 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:
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,
// 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)
+ 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)
+ if (!filename) {
+ ERROR(transaction->pakfire, "Could not retrieve filename for package %s: %m\n", nevra);
goto ERROR;
+ }
- const char* nevra = pakfire_package_get_nevra(pkg);
- if (!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, 0);
+ nevra, filename, path, digest_type, digest, digest_length, 0);
ERROR:
if (mirrorlist)
// Enqueue download
r = pakfire_transaction_download_package(transaction, downloader, pkg);
- if (r)
+ 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
return r;
}
-PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transaction) {
+PAKFIRE_EXPORT int pakfire_transaction_run(
+ struct pakfire_transaction* transaction, int flags) {
int r;
// Skip running an empty transaction
char* dump = pakfire_transaction_dump(transaction, 80);
// Check if we should continue
- r = pakfire_ui_confirm(transaction->pakfire, dump, _("Is this okay? [y/N]"));
+ 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;
ERROR:
+ // Cleanup
free(dump);
return r;