packagekit/backends/alpm/pk-alpm-transaction.c
2024-09-21 12:33:58 -04:00

1136 lines
30 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 Andreas Obergrusberger <tradiaz@yahoo.de>
* Copyright (C) 2008-2010 Valeriy Lyasotskiy <onestep@ukr.net>
* Copyright (C) 2010-2011 Jonathan Conder <jonno.conder@gmail.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pk-backend-alpm.h"
#include "pk-alpm-error.h"
#include "pk-alpm-packages.h"
#include "pk-alpm-transaction.h"
#include <syslog.h>
static off_t transaction_dcomplete = 0;
static off_t transaction_dtotal = 0;
static alpm_pkg_t *dpkg = NULL;
static GString *dfiles = NULL;
static alpm_pkg_t *tpkg = NULL;
static GString *toutput = NULL;
static PkBackendJob* pkalpm_current_job = NULL;
const gchar *pkalpm_dirname = NULL;
static gchar *
pk_alpm_resolve_path (PkBackendJob *job, const gchar *basename)
{
g_return_val_if_fail (job != NULL, NULL);
g_return_val_if_fail (basename != NULL, NULL);
g_return_val_if_fail (pkalpm_dirname != NULL, NULL);
return g_build_filename (pkalpm_dirname, basename, NULL);
}
static gboolean
pk_alpm_pkg_has_basename (PkBackend *backend, alpm_pkg_t *pkg, const gchar *basename)
{
g_return_val_if_fail (pkg != NULL, FALSE);
g_return_val_if_fail (basename != NULL, FALSE);
if (g_strcmp0 (alpm_pkg_get_filename (pkg), basename) == 0)
return TRUE;
return FALSE;
}
static void
pk_alpm_transaction_download_end (PkBackendJob *job)
{
g_return_if_fail (dpkg != NULL);
pk_alpm_pkg_emit (job, dpkg, PK_INFO_ENUM_FINISHED);
/* tell DownloadPackages what files were downloaded */
if (dfiles != NULL) {
g_autofree gchar *package_id = pk_alpm_pkg_build_id (dpkg);
pk_backend_job_files (job, package_id, &dfiles->str);
g_string_free (dfiles, TRUE);
}
dpkg = NULL;
dfiles = NULL;
}
static void
pk_alpm_transaction_download_start (PkBackendJob *job, const gchar *basename)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
const alpm_list_t *i;
g_return_if_fail (basename != NULL);
/* continue or finish downloading the current package */
if (dpkg != NULL) {
if (pk_alpm_pkg_has_basename (backend, dpkg, basename)) {
if (dfiles != NULL) {
g_autofree gchar *path = NULL;
path = pk_alpm_resolve_path (job, basename);
g_string_append_printf (dfiles, ";%s", path);
}
return;
}
pk_alpm_transaction_download_end (job);
dpkg = NULL;
}
/* figure out what the next package is */
for (i = alpm_trans_get_add (priv->alpm); i != NULL; i = i->next) {
alpm_pkg_t *pkg = (alpm_pkg_t *) i->data;
if (pk_alpm_pkg_has_basename (backend, pkg, basename)) {
dpkg = pkg;
break;
}
}
if (dpkg == NULL)
return;
pk_alpm_pkg_emit (job, dpkg, PK_INFO_ENUM_DOWNLOADING);
/* start collecting files for the new package */
if (pk_backend_job_get_role (job) == PK_ROLE_ENUM_DOWNLOAD_PACKAGES) {
g_autofree gchar *path = NULL;
path = pk_alpm_resolve_path (job, basename);
dfiles = g_string_new (path);
}
}
static void
pk_alpm_transaction_dlcb (void *ctx, const gchar *filename, alpm_download_event_type_t type, void *data)
{
guint percentage = 100, sub_percentage = 100;
alpm_download_event_completed_t *completed = data;
alpm_download_event_progress_t *progress = data;
PkBackendJob* job;
g_assert (pkalpm_current_job);
job = pkalpm_current_job;
g_return_if_fail (filename != NULL);
switch (type) {
case ALPM_DOWNLOAD_INIT:
pk_backend_job_set_status (job, PK_STATUS_ENUM_DOWNLOAD);
pk_alpm_transaction_download_start (job, filename);
break;
case ALPM_DOWNLOAD_COMPLETED:
pk_backend_job_set_percentage (job, 100);
transaction_dcomplete += completed->total;
break;
case ALPM_DOWNLOAD_PROGRESS:
if (transaction_dtotal > 0) {
transaction_dcomplete += progress->downloaded;
percentage = ((transaction_dcomplete + progress->downloaded) * 100) / transaction_dtotal;
pk_backend_job_set_percentage (job, percentage);
} else if (transaction_dtotal < 0) {
static off_t previous_total = 0;
static guint current_database = 0;
guint total_databases = -transaction_dtotal;
if (progress->total != previous_total) {
current_database++;
previous_total = progress->total;
}
percentage = ((current_database - 1) * 100) / total_databases;
percentage += sub_percentage / total_databases;
pk_backend_job_set_percentage (job, percentage);
}
break;
default:
syslog (LOG_DAEMON | LOG_WARNING, "unhandled download callback case, most likely libalpm change or error");
}
}
static void
pk_alpm_transaction_progress_cb (void *ctx, alpm_progress_t type, const gchar *target,
gint percent, gsize targets, gsize current)
{
static gint recent = 101;
gsize overall = percent + (current - 1) * 100;
PkBackendJob* job;
g_assert (pkalpm_current_job);
job = pkalpm_current_job;
if (g_strcmp0(target, "") == 0) {
switch (type) {
case ALPM_PROGRESS_KEYRING_START:
pk_backend_job_set_status(job, PK_STATUS_ENUM_SIG_CHECK);
pk_backend_job_set_percentage(job, percent);
break;
case ALPM_PROGRESS_INTEGRITY_START:
pk_backend_job_set_status(job, PK_STATUS_ENUM_SIG_CHECK);
pk_backend_job_set_percentage(job, percent);
break;
case ALPM_PROGRESS_LOAD_START:
pk_backend_job_set_status(job, PK_STATUS_ENUM_LOADING_CACHE);
pk_backend_job_set_percentage(job, percent);
break;
case ALPM_PROGRESS_DISKSPACE_START:
pk_backend_job_set_status(job, PK_STATUS_ENUM_TEST_COMMIT);
pk_backend_job_set_percentage(job, percent);
break;
case ALPM_PROGRESS_CONFLICTS_START:
pk_backend_job_set_status(job, PK_STATUS_ENUM_TEST_COMMIT);
pk_backend_job_set_percentage(job, percent);
break;
default:
syslog (LOG_DAEMON | LOG_WARNING, "unhandled progress type for transaction %d", type);
break;
}
}
/* TODO: remove block if/when this is made consistent upstream */
if (type == ALPM_PROGRESS_CONFLICTS_START ||
type == ALPM_PROGRESS_DISKSPACE_START ||
type == ALPM_PROGRESS_INTEGRITY_START ||
type == ALPM_PROGRESS_LOAD_START ||
type == ALPM_PROGRESS_KEYRING_START) {
if (current < targets) {
++current;
overall += 100;
}
}
if (current < 1 || targets < current)
syslog (LOG_DAEMON | LOG_WARNING, "TODO: CURRENT/TARGETS FAILED for %d", type);
g_return_if_fail (target != NULL);
g_return_if_fail (0 <= percent && percent <= 100);
g_return_if_fail (1 <= current && current <= targets);
/* update transaction progress */
switch (type) {
case ALPM_PROGRESS_ADD_START:
case ALPM_PROGRESS_UPGRADE_START:
case ALPM_PROGRESS_DOWNGRADE_START:
case ALPM_PROGRESS_REINSTALL_START:
case ALPM_PROGRESS_REMOVE_START:
if (percent == recent)
break;
pk_backend_job_set_item_progress (job, target, PK_STATUS_ENUM_UNKNOWN, percent);
pk_backend_job_set_percentage (job, overall / targets);
recent = percent;
syslog (LOG_DAEMON | LOG_WARNING, "%d%% of %s complete (%zu of %zu)", percent,
target, current, targets);
break;
default:
syslog (LOG_DAEMON | LOG_WARNING, "unknown progress type %d", type);
break;
}
}
static void
pk_alpm_install_ignorepkg (PkBackendJob *job, alpm_question_install_ignorepkg_t *q)
{
g_autofree gchar *output = NULL;
g_return_if_fail (q != NULL);
g_return_if_fail (q->pkg != NULL);
switch (pk_backend_job_get_role (job)) {
case PK_ROLE_ENUM_INSTALL_PACKAGES:
output = g_strdup_printf ("%s: was not ignored\n",
alpm_pkg_get_name (q->pkg));
pk_alpm_transaction_output (output);
#if (!defined(__clang__)) && (__GNUC__ >= 7)
__attribute__ ((fallthrough)); /* let's be explicit about falltrhough */
#endif
case PK_ROLE_ENUM_DOWNLOAD_PACKAGES:
q->install = 1;
break;
default:
q->install = 0;
break;
}
}
static void
pk_alpm_select_provider (const alpm_list_t *providers,
alpm_depend_t *depend)
{
g_autofree gchar *output = NULL;
g_return_if_fail (depend != NULL);
g_return_if_fail (providers != NULL);
output = g_strdup_printf ("provider package was selected "
"(%s provides %s)\n",
alpm_pkg_get_name (providers->data),
depend->name);
pk_alpm_transaction_output (output);
}
static void
pk_alpm_transaction_conv_cb (void *ctx, alpm_question_t *question)
{
PkBackendJob* job;
g_assert (pkalpm_current_job);
job = pkalpm_current_job;
g_return_if_fail (question != NULL);
switch (question->type) {
case ALPM_QUESTION_INSTALL_IGNOREPKG:
{
alpm_question_install_ignorepkg_t *q = &question->install_ignorepkg;
pk_alpm_install_ignorepkg (job, q);
}
break;
case ALPM_QUESTION_REPLACE_PKG:
{
alpm_question_replace_t *q = &question->replace;
g_debug ("safe question %d", question->type);
q->replace = 1;
}
break;
case ALPM_QUESTION_CONFLICT_PKG:
case ALPM_QUESTION_CORRUPTED_PKG:
{
alpm_question_conflict_t *q = &question->conflict;
g_debug ("safe question %d", question->type);
q->remove = 1;
}
break;
// case ALPM_QUESTION_LOCAL_NEWER:
case ALPM_QUESTION_REMOVE_PKGS:
{
alpm_question_remove_pkgs_t *q = &question->remove_pkgs;
g_debug ("unsafe question %d", question->type);
q->skip = 0;
}
break;
/* TODO: handle keys better */
case ALPM_QUESTION_IMPORT_KEY:
{
alpm_question_import_key_t *q = &question->import_key;
g_debug ("unsafe question %d", question->type);
q->import = 0;
}
break;
case ALPM_QUESTION_SELECT_PROVIDER:
{
alpm_question_select_provider_t *q = &question->select_provider;
pk_alpm_select_provider (q->providers, q->depend);
q->use_index = 0;
}
break;
default:
syslog (LOG_DAEMON | LOG_WARNING, "unknown question %d", question->type);
break;
}
}
static void
pk_alpm_transaction_output_end (void)
{
tpkg = NULL;
if (toutput != NULL) {
pk_alpm_transaction_output (toutput->str);
g_string_free (toutput, TRUE);
toutput = NULL;
}
}
static void
pk_alpm_transaction_output_start (alpm_pkg_t *pkg)
{
g_return_if_fail (pkg != NULL);
if (tpkg != NULL)
pk_alpm_transaction_output_end ();
tpkg = pkg;
}
void
pk_alpm_transaction_output (const gchar *output)
{
g_return_if_fail (output != NULL);
if (tpkg != NULL) {
if (toutput == NULL) {
toutput = g_string_new ("<b>");
g_string_append (toutput, alpm_pkg_get_name (tpkg));
g_string_append (toutput, "</b>\n");
}
g_string_append (toutput, output);
}
}
static void
pk_alpm_transaction_dep_resolve (PkBackendJob *job)
{
pk_backend_job_set_status (job, PK_STATUS_ENUM_DEP_RESOLVE);
}
static void
pk_alpm_transaction_hook (PkBackendJob *job)
{
pk_backend_job_set_status (job, PK_STATUS_ENUM_RUN_HOOK);
pk_backend_job_set_percentage (job, 0);
}
static void
pk_alpm_transaction_hook_run (PkBackendJob *job, alpm_event_hook_run_t * event)
{
/* Every hook runs a single command, so there is no progress.
Instead calculate the progress from total and finished hooks. */
pk_backend_job_set_percentage (job, 100 * event->position / event->total);
syslog (LOG_DAEMON | LOG_WARNING, "Hook %s (%s) complete (%zu of %zu)",
event->name, event->desc, event->position, event->total);
}
static void
pk_alpm_transaction_test_commit (PkBackendJob *job)
{
pk_backend_job_set_status (job, PK_STATUS_ENUM_TEST_COMMIT);
}
static void
pk_alpm_transaction_add_start (PkBackendJob *job, alpm_pkg_t *pkg)
{
g_return_if_fail (pkg != NULL);
pk_backend_job_set_status (job, PK_STATUS_ENUM_INSTALL);
pk_alpm_pkg_emit (job, pkg, PK_INFO_ENUM_INSTALLING);
pk_alpm_transaction_output_start (pkg);
}
static void
pk_alpm_transaction_add_done (PkBackendJob *job, alpm_pkg_t *pkg)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
const gchar *name, *version;
const alpm_list_t *i, *optdepends;
g_return_if_fail (pkg != NULL);
name = alpm_pkg_get_name (pkg);
version = alpm_pkg_get_version (pkg);
alpm_logaction (priv->alpm, PK_LOG_PREFIX, "installed %s (%s)\n", name,
version);
pk_alpm_pkg_emit (job, pkg, PK_INFO_ENUM_FINISHED);
optdepends = alpm_pkg_get_optdepends (pkg);
if (optdepends != NULL) {
pk_alpm_transaction_output ("Optional dependencies:\n");
for (i = optdepends; i != NULL; i = i->next) {
char *depend = alpm_dep_compute_string (i->data);
g_autofree gchar *output = g_strdup_printf ("%s\n", depend);
free (depend);
pk_alpm_transaction_output (output);
}
}
pk_alpm_transaction_output_end ();
}
static void
pk_alpm_transaction_remove_start (PkBackendJob *job, alpm_pkg_t *pkg)
{
g_return_if_fail (pkg != NULL);
pk_backend_job_set_status (job, PK_STATUS_ENUM_REMOVE);
pk_alpm_pkg_emit (job, pkg, PK_INFO_ENUM_REMOVING);
pk_alpm_transaction_output_start (pkg);
}
static void
pk_alpm_transaction_remove_done (PkBackendJob *job, alpm_pkg_t *pkg)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
const gchar *name, *version;
g_return_if_fail (pkg != NULL);
name = alpm_pkg_get_name (pkg);
version = alpm_pkg_get_version (pkg);
alpm_logaction (priv->alpm, PK_LOG_PREFIX, "removed %s (%s)\n", name, version);
pk_alpm_pkg_emit (job, pkg, PK_INFO_ENUM_FINISHED);
pk_alpm_transaction_output_end ();
}
static void
pk_alpm_transaction_upgrade_start (PkBackendJob *job, alpm_pkg_t *pkg,
alpm_pkg_t *old)
{
PkRoleEnum role;
PkStatusEnum state;
PkInfoEnum info;
g_return_if_fail (pkg != NULL);
role = pk_backend_job_get_role (job);
if (role == PK_ROLE_ENUM_INSTALL_FILES) {
state = PK_STATUS_ENUM_INSTALL;
info = PK_INFO_ENUM_INSTALLING;
} else {
state = PK_STATUS_ENUM_UPDATE;
info = PK_INFO_ENUM_UPDATING;
}
pk_backend_job_set_status (job, state);
pk_alpm_pkg_emit (job, pkg, info);
pk_alpm_transaction_output_start (pkg);
}
static gint
pk_alpm_depend_compare (gconstpointer a, gconstpointer b)
{
const alpm_depend_t *first = a;
const alpm_depend_t *second = b;
gint result;
g_return_val_if_fail (first != NULL, 0);
g_return_val_if_fail (second != NULL, 0);
result = g_strcmp0 (first->name, second->name);
if (result == 0) {
result = first->mod - second->mod;
if (result == 0) {
result = g_strcmp0 (first->version, second->version);
if (result == 0)
result = g_strcmp0 (first->desc, second->desc);
}
}
return result;
}
static void
pk_alpm_transaction_process_new_optdepends (alpm_pkg_t *pkg, alpm_pkg_t *old)
{
alpm_list_t *optdepends;
const alpm_list_t *i;
g_return_if_fail (pkg != NULL);
g_return_if_fail (old != NULL);
optdepends = alpm_list_diff (alpm_pkg_get_optdepends (pkg),
alpm_pkg_get_optdepends (old),
pk_alpm_depend_compare);
if (optdepends == NULL)
return;
pk_alpm_transaction_output ("New optional dependencies:\n");
for (i = optdepends; i != NULL; i = i->next) {
char *depend = alpm_dep_compute_string (i->data);
g_autofree gchar *output = g_strdup_printf ("%s\n", depend);
free (depend);
pk_alpm_transaction_output (output);
}
alpm_list_free (optdepends);
}
static void
pk_alpm_transaction_upgrade_done (PkBackendJob *job, alpm_pkg_t *pkg,
alpm_pkg_t *old, gint direction)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
const gchar *name, *pre, *post;
g_return_if_fail (pkg != NULL);
g_return_if_fail (old != NULL || direction == ALPM_PACKAGE_REINSTALL);
name = alpm_pkg_get_name (pkg);
if (direction != ALPM_PACKAGE_REINSTALL)
pre = alpm_pkg_get_version (old);
post = alpm_pkg_get_version (pkg);
if (direction == ALPM_PACKAGE_UPGRADE) {
alpm_logaction (priv->alpm, PK_LOG_PREFIX, "upgraded %s (%s -> %s)\n",
name, pre, post);
} else if (direction == ALPM_PACKAGE_DOWNGRADE) {
alpm_logaction (priv->alpm, PK_LOG_PREFIX,
"downgraded %s (%s -> %s)\n", name, pre, post);
} else {
alpm_logaction (priv->alpm, PK_LOG_PREFIX, "reinstalled %s (%s)\n",
name, post);
}
pk_alpm_pkg_emit (job, pkg, PK_INFO_ENUM_FINISHED);
if (direction != ALPM_PACKAGE_REINSTALL)
pk_alpm_transaction_process_new_optdepends (pkg, old);
pk_alpm_transaction_output_end ();
}
static void
pk_alpm_transaction_sig_check (PkBackendJob *job)
{
pk_backend_job_set_status (job, PK_STATUS_ENUM_SIG_CHECK);
}
static void
pk_alpm_transaction_setup (PkBackendJob *job)
{
pk_backend_job_set_status (job, PK_STATUS_ENUM_SETUP);
}
static void
pk_alpm_transaction_download (PkBackendJob *job)
{
pk_backend_job_set_status (job, PK_STATUS_ENUM_DOWNLOAD);
}
static void
pk_alpm_transaction_optdepend_removal (PkBackendJob *job, alpm_pkg_t *pkg,
alpm_depend_t *optdepend)
{
char *depend = NULL;
g_autofree gchar *output = NULL;
g_return_if_fail (pkg != NULL);
g_return_if_fail (optdepend != NULL);
depend = alpm_dep_compute_string (optdepend);
output = g_strdup_printf ("optionally requires %s\n", depend);
free (depend);
// pk_backend_job_message (job, pkg, output);
pk_backend_job_error_code (job, PK_ERROR_ENUM_DEP_RESOLUTION_FAILED,
"%s\n%s", alpm_pkg_get_name (pkg), output);
}
static void
pk_alpm_transaction_event_cb (void *ctx, alpm_event_t *event)
{
PkBackendJob* job;
job = pkalpm_current_job;
g_assert (job);
/* figure out backend status and process package changes */
switch (event->type) {
case ALPM_EVENT_CHECKDEPS_START:
case ALPM_EVENT_RESOLVEDEPS_START:
pk_alpm_transaction_dep_resolve (job);
break;
case ALPM_EVENT_DISKSPACE_START:
case ALPM_EVENT_FILECONFLICTS_START:
case ALPM_EVENT_INTERCONFLICTS_START:
pk_alpm_transaction_test_commit (job);
break;
case ALPM_EVENT_PACKAGE_OPERATION_START:
{
alpm_event_package_operation_t *e = (alpm_event_package_operation_t *) event;
switch(e->operation) {
case ALPM_PACKAGE_INSTALL:
pk_alpm_transaction_add_start (job, e->newpkg);
break;
case ALPM_PACKAGE_REMOVE:
pk_alpm_transaction_remove_start (job, e->oldpkg);
break;
case ALPM_PACKAGE_UPGRADE:
case ALPM_PACKAGE_DOWNGRADE:
case ALPM_PACKAGE_REINSTALL:
pk_alpm_transaction_upgrade_start (job, e->newpkg, e->oldpkg);
break;
}
}
break;
case ALPM_EVENT_PACKAGE_OPERATION_DONE:
{
alpm_event_package_operation_t *e = (alpm_event_package_operation_t *) event;
switch(e->operation) {
case ALPM_PACKAGE_INSTALL:
pk_alpm_transaction_add_done (job, e->newpkg);
break;
case ALPM_PACKAGE_REMOVE:
pk_alpm_transaction_remove_done (job, e->oldpkg);
break;
case ALPM_PACKAGE_UPGRADE:
pk_alpm_transaction_upgrade_done (job, e->newpkg, e->oldpkg, ALPM_PACKAGE_UPGRADE);
break;
case ALPM_PACKAGE_DOWNGRADE:
pk_alpm_transaction_upgrade_done (job, e->newpkg, e->oldpkg, ALPM_PACKAGE_DOWNGRADE);
break;
case ALPM_PACKAGE_REINSTALL:
pk_alpm_transaction_upgrade_done (job, e->newpkg, e->oldpkg, ALPM_PACKAGE_REINSTALL);
break;
}
}
break;
case ALPM_EVENT_INTEGRITY_START:
case ALPM_EVENT_KEYRING_START:
pk_alpm_transaction_sig_check (job);
break;
case ALPM_EVENT_LOAD_START:
pk_alpm_transaction_setup (job);
break;
case ALPM_EVENT_SCRIPTLET_INFO:
pk_alpm_transaction_output (((alpm_event_scriptlet_info_t *) event)->line);
break;
case ALPM_EVENT_KEY_DOWNLOAD_START:
case ALPM_EVENT_DB_RETRIEVE_START:
pk_alpm_transaction_download (job);
break;
case ALPM_EVENT_OPTDEP_REMOVAL:
/* TODO: remove if this results in notification spam */
{
alpm_event_optdep_removal_t *e = (alpm_event_optdep_removal_t *) event;
pk_alpm_transaction_optdepend_removal (job, e->pkg, e->optdep);
}
break;
case ALPM_EVENT_HOOK_START:
pk_alpm_transaction_hook (job);
break;
case ALPM_EVENT_HOOK_RUN_DONE:
pk_alpm_transaction_hook_run (job, (alpm_event_hook_run_t *)event);
break;
case ALPM_EVENT_CHECKDEPS_DONE:
case ALPM_EVENT_DATABASE_MISSING:
case ALPM_EVENT_DISKSPACE_DONE:
case ALPM_EVENT_FILECONFLICTS_DONE:
case ALPM_EVENT_HOOK_DONE:
case ALPM_EVENT_HOOK_RUN_START:
case ALPM_EVENT_INTEGRITY_DONE:
case ALPM_EVENT_INTERCONFLICTS_DONE:
case ALPM_EVENT_KEY_DOWNLOAD_DONE:
case ALPM_EVENT_KEYRING_DONE:
case ALPM_EVENT_LOAD_DONE:
case ALPM_EVENT_PACNEW_CREATED:
case ALPM_EVENT_PACSAVE_CREATED:
case ALPM_EVENT_PKG_RETRIEVE_DONE:
case ALPM_EVENT_PKG_RETRIEVE_FAILED:
case ALPM_EVENT_PKG_RETRIEVE_START:
case ALPM_EVENT_RESOLVEDEPS_DONE:
case ALPM_EVENT_DB_RETRIEVE_DONE:
case ALPM_EVENT_DB_RETRIEVE_FAILED:
case ALPM_EVENT_TRANSACTION_DONE:
case ALPM_EVENT_TRANSACTION_START:
/* ignored */
break;
default:
syslog (LOG_DAEMON | LOG_WARNING, "unhandled event %d", event->type);
break;
}
}
static void
pk_alpm_transaction_cancelled_cb (GCancellable *object, gpointer data)
{
PkBackend *backend = pk_backend_job_get_backend (PK_BACKEND_JOB (data));
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
alpm_trans_interrupt (priv->alpm);
}
gboolean
pk_alpm_transaction_initialize (PkBackendJob* job, alpm_transflag_t flags, const gchar* dirname, GError** error)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
if (alpm_trans_init (priv->alpm, flags) < 0) {
alpm_errno_t alpm_err = alpm_errno (priv->alpm);
g_set_error_literal (error, PK_ALPM_ERROR, alpm_err,
alpm_strerror (alpm_err));
return FALSE;
}
g_assert (pkalpm_current_job == NULL);
pkalpm_current_job = job;
pkalpm_dirname = dirname;
alpm_option_set_eventcb (priv->alpm, pk_alpm_transaction_event_cb, NULL);
alpm_option_set_questioncb (priv->alpm, pk_alpm_transaction_conv_cb, NULL);
alpm_option_set_progresscb (priv->alpm, pk_alpm_transaction_progress_cb, NULL);
alpm_option_set_dlcb (priv->alpm, pk_alpm_transaction_dlcb, NULL);
g_cancellable_connect (pk_backend_job_get_cancellable (job),
G_CALLBACK (pk_alpm_transaction_cancelled_cb),
job, NULL);
return TRUE;
}
static gchar *
pk_alpm_pkg_build_list (const alpm_list_t *i)
{
GString *list;
if (i == NULL)
return NULL;
list = g_string_new ("");
for (; i != NULL; i = i->next) {
if (i->data == NULL)
continue;
g_string_append_printf (list, "%s, ",
alpm_pkg_get_name (i->data));
}
if (list->len > 2)
g_string_truncate (list, list->len - 2);
return g_string_free (list, FALSE);
}
static gchar *
pk_alpm_miss_build_list (const alpm_list_t *i)
{
GString *list;
if (i == NULL)
return NULL;
list = g_string_new ("");
for (; i != NULL; i = i->next) {
alpm_depmissing_t *miss = (alpm_depmissing_t *) i->data;
char *depend = alpm_dep_compute_string (miss->depend);
g_string_append_printf (list, "%s <- %s, ", depend,
miss->target);
free (depend);
}
g_string_truncate (list, list->len - 2);
return g_string_free (list, FALSE);
}
static void
pk_alpm_depend_free (alpm_depend_t *depend)
{
free (depend->name);
free (depend->version);
free (depend->desc);
free (depend);
}
static void
pk_alpm_depmissing_free (gpointer miss)
{
alpm_depmissing_t *self = (alpm_depmissing_t *) miss;
free (self->target);
pk_alpm_depend_free (self->depend);
free (self->causingpkg);
free (miss);
}
static gchar *
pk_alpm_conflict_build_list (const alpm_list_t *i)
{
GString *list;
if (i == NULL)
return NULL;
list = g_string_new ("");
for (; i != NULL; i = i->next) {
alpm_conflict_t *conflict = (alpm_conflict_t *) i->data;
alpm_depend_t *depend = conflict->reason;
const char *package_name1 = alpm_pkg_get_name (conflict->package1);
const char *package_name2 = alpm_pkg_get_name (conflict->package2);
if (g_strcmp0 (package_name1, depend->name) == 0 ||
g_strcmp0 (package_name2, depend->name) == 0) {
g_string_append_printf (list, "%s <-> %s, ",
package_name1,
package_name2);
} else {
char *reason = alpm_dep_compute_string (depend);
g_string_append_printf (list, "%s <-> %s (%s), ",
package_name1,
package_name2, reason);
free (reason);
}
}
g_string_truncate (list, list->len - 2);
return g_string_free (list, FALSE);
}
static void
pk_alpm_conflict_free (gpointer conflict)
{
alpm_conflict_t *self = (alpm_conflict_t *) conflict;
free (self->package1);
free (self->package2);
free (conflict);
}
static gchar *
pk_alpm_fileconflict_build_list (const alpm_list_t *i)
{
GString *list;
if (i == NULL)
return NULL;
list = g_string_new ("");
for (; i != NULL; i = i->next) {
alpm_fileconflict_t *conflict = (alpm_fileconflict_t *) i->data;
if (*conflict->ctarget != '\0') {
g_string_append_printf (list, "%s <-> %s (%s), ",
conflict->target,
conflict->ctarget,
conflict->file);
} else {
g_string_append_printf (list, "%s (%s), ",
conflict->target,
conflict->file);
}
}
g_string_truncate (list, list->len - 2);
return g_string_free (list, FALSE);
}
static void
pk_alpm_fileconflict_free (gpointer conflict)
{
alpm_fileconflict_t *self = (alpm_fileconflict_t *) conflict;
free (self->target);
free (self->file);
free (self->ctarget);
free (conflict);
}
gboolean
pk_alpm_transaction_simulate (PkBackendJob *job, GError **error)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
alpm_list_t *data = NULL;
g_autofree gchar *prefix = NULL;
if (alpm_trans_prepare (priv->alpm, &data) >= 0)
return TRUE;
switch (alpm_errno (priv->alpm)) {
case ALPM_ERR_PKG_INVALID_ARCH:
prefix = pk_alpm_pkg_build_list (data);
alpm_list_free (data);
break;
case ALPM_ERR_UNSATISFIED_DEPS:
prefix = pk_alpm_miss_build_list (data);
alpm_list_free_inner (data, pk_alpm_depmissing_free);
alpm_list_free (data);
break;
case ALPM_ERR_CONFLICTING_DEPS:
prefix = pk_alpm_conflict_build_list (data);
alpm_list_free_inner (data, pk_alpm_conflict_free);
alpm_list_free (data);
break;
case ALPM_ERR_FILE_CONFLICTS:
prefix = pk_alpm_fileconflict_build_list (data);
alpm_list_free_inner (data, pk_alpm_fileconflict_free);
alpm_list_free (data);
break;
default:
if (data != NULL)
syslog (LOG_DAEMON | LOG_WARNING, "unhandled error %d", alpm_errno (priv->alpm));
break;
}
if (prefix != NULL) {
alpm_errno_t alpm_err = alpm_errno (priv->alpm);
g_set_error (error, PK_ALPM_ERROR, alpm_err, "%s: %s", prefix,
alpm_strerror (alpm_err));
} else {
alpm_errno_t alpm_err = alpm_errno (priv->alpm);
g_set_error_literal (error, PK_ALPM_ERROR, alpm_err,
alpm_strerror (alpm_err));
}
return FALSE;
}
void
pk_alpm_transaction_packages (PkBackendJob *job)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
const alpm_list_t *i;
PkInfoEnum info;
/* emit packages that would have been installed */
for (i = alpm_trans_get_add (priv->alpm); i != NULL; i = i->next) {
const gchar *name;
if (pk_backend_job_is_cancelled (job))
break;
name = alpm_pkg_get_name (i->data);
if (alpm_db_get_pkg (priv->localdb, name) != NULL) {
info = PK_INFO_ENUM_UPDATING;
} else {
info = PK_INFO_ENUM_INSTALLING;
}
pk_alpm_pkg_emit (job, i->data, info);
}
switch (pk_backend_job_get_role (job)) {
case PK_ROLE_ENUM_UPDATE_PACKAGES:
info = PK_INFO_ENUM_OBSOLETING;
break;
default:
info = PK_INFO_ENUM_REMOVING;
break;
}
/* emit packages that would have been removed */
for (i = alpm_trans_get_remove (priv->alpm); i != NULL; i = i->next) {
if (pk_backend_job_is_cancelled (job))
break;
pk_alpm_pkg_emit (job, i->data, info);
}
}
static gchar *
pk_alpm_string_build_list (const alpm_list_t *i)
{
GString *list;
if (i == NULL)
return NULL;
list = g_string_new ("");
for (; i != NULL; i = i->next)
g_string_append_printf (list, "%s, ", (const gchar *) i->data);
g_string_truncate (list, list->len - 2);
return g_string_free (list, FALSE);
}
gboolean
pk_alpm_transaction_commit (PkBackendJob *job, GError **error)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
alpm_list_t *data = NULL;
g_autofree gchar *prefix = NULL;
gint commit_result;
if (pk_backend_job_is_cancelled (job))
return TRUE;
pk_backend_job_set_allow_cancel (job, FALSE);
pk_backend_job_set_status (job, PK_STATUS_ENUM_RUNNING);
pk_backend_transaction_inhibit_start (backend);
commit_result = alpm_trans_commit (priv->alpm, &data);
pk_backend_transaction_inhibit_end (backend);
if (commit_result >= 0)
return TRUE;
switch (alpm_errno (priv->alpm)) {
case ALPM_ERR_FILE_CONFLICTS:
prefix = pk_alpm_fileconflict_build_list (data);
alpm_list_free_inner (data, pk_alpm_fileconflict_free);
alpm_list_free (data);
break;
case ALPM_ERR_PKG_INVALID:
prefix = pk_alpm_string_build_list (data);
alpm_list_free (data);
break;
default:
if (data != NULL) {
syslog (LOG_DAEMON | LOG_WARNING, "unhandled error %d",
alpm_errno (priv->alpm));
}
break;
}
if (prefix != NULL) {
alpm_errno_t alpm_err = alpm_errno (priv->alpm);
g_set_error (error, PK_ALPM_ERROR, alpm_err, "%s: %s", prefix,
alpm_strerror (alpm_err));
} else {
alpm_errno_t alpm_err = alpm_errno (priv->alpm);
g_set_error_literal (error, PK_ALPM_ERROR, alpm_err,
alpm_strerror (alpm_err));
}
return FALSE;
}
gboolean
pk_alpm_transaction_end (PkBackendJob *job, GError **error)
{
PkBackend *backend = pk_backend_job_get_backend (job);
PkBackendAlpmPrivate *priv = pk_backend_get_user_data (backend);
alpm_option_set_eventcb (priv->alpm, NULL, NULL);
alpm_option_set_questioncb (priv->alpm, NULL, NULL);
alpm_option_set_progresscb (priv->alpm, NULL, NULL);
alpm_option_set_dlcb (priv->alpm, NULL, NULL);
// alpm_option_set_totaldlcb (priv->alpm, NULLa;
if (dpkg != NULL)
pk_alpm_transaction_download_end (job);
if (tpkg != NULL)
pk_alpm_transaction_output_end ();
g_assert (pkalpm_current_job);
pkalpm_current_job = NULL;
if (alpm_trans_release (priv->alpm) < 0) {
alpm_errno_t alpm_err = alpm_errno (priv->alpm);
g_set_error_literal (error, PK_ALPM_ERROR, alpm_err,
alpm_strerror (alpm_err));
return FALSE;
}
return TRUE;
}
gboolean
pk_alpm_transaction_finish (PkBackendJob *job, GError *error)
{
pk_alpm_transaction_end (job, (error == NULL) ? &error : NULL);
return pk_alpm_finish (job, error);
}