packagekit/src/pk-backend.c

1547 lines
44 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Richard Hughes <richard@hughsie.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <glib/gi18n.h>
#include <glib.h>
#include <gmodule.h>
#include <glib/gprintf.h>
#include "pk-debug.h"
#include "pk-common.h"
#include "pk-marshal.h"
#include "pk-backend-internal.h"
#include "pk-backend.h"
#include "pk-time.h"
#include "pk-inhibit.h"
#define PK_BACKEND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_BACKEND, PkBackendPrivate))
/**
* PK_BACKEND_PERCENTAGE_INVALID:
*
* The unknown percentage value
*/
#define PK_BACKEND_PERCENTAGE_INVALID 101
/**
* PK_BACKEND_PERCENTAGE_DEFAULT:
*
* The default percentage value, should never be emitted, but should be
* used so we can work out if a backend just calls NoPercentageUpdates
*/
#define PK_BACKEND_PERCENTAGE_DEFAULT 102
/**
* PK_BACKEND_FINISHED_ERROR_TIMEOUT:
*
* The time in ms the backend has to call Finished() after ErrorCode()
* If backends do not do this, they will be Finished() manually,
* and a Message() will be sent to warn the developer
*/
#define PK_BACKEND_FINISHED_ERROR_TIMEOUT 500 /* ms */
/**
* PK_BACKEND_FINISHED_TIMEOUT_GRACE:
*
* The time in ms the backend waits after receiving Finished() before
* propagating the signal to the other components.
* This delay is required as some threads may take some time to cancel or a
* spawned executable to disappear of the system DBUS.
*/
#define PK_BACKEND_FINISHED_TIMEOUT_GRACE 50 /* ms */
struct _PkBackendPrivate
{
GModule *handle;
PkTime *time;
gchar *name;
gchar *c_tid;
gboolean locked;
gboolean set_error;
gboolean set_signature;
PkRoleEnum role; /* this never changes for the lifetime of a transaction */
PkStatusEnum status; /* this changes */
PkExitEnum exit;
PkInhibit *inhibit;
gboolean during_initialize;
gboolean allow_cancel;
gboolean finished;
guint last_percentage;
guint last_subpercentage;
guint last_remaining;
guint signal_finished;
guint signal_error_timeout;
};
G_DEFINE_TYPE (PkBackend, pk_backend, G_TYPE_OBJECT)
static gpointer pk_backend_object = NULL;
enum {
PK_BACKEND_STATUS_CHANGED,
PK_BACKEND_PROGRESS_CHANGED,
PK_BACKEND_DESCRIPTION,
PK_BACKEND_FILES,
PK_BACKEND_PACKAGE,
PK_BACKEND_UPDATE_DETAIL,
PK_BACKEND_ERROR_CODE,
PK_BACKEND_REPO_SIGNATURE_REQUIRED,
PK_BACKEND_REQUIRE_RESTART,
PK_BACKEND_MESSAGE,
PK_BACKEND_CHANGE_TRANSACTION_DATA,
PK_BACKEND_FINISHED,
PK_BACKEND_ALLOW_CANCEL,
PK_BACKEND_REPO_DETAIL,
PK_BACKEND_LAST_SIGNAL
};
static guint signals [PK_BACKEND_LAST_SIGNAL] = { 0 };
/**
* pk_backend_set_internal:
*
* Designed for volatile internal state, such as the authentication prompt
* response, the proxy to use and that sort of thing
**/
gboolean
pk_backend_set_internal (PkBackend *backend, const gchar *key, const gchar *data)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
return FALSE;
}
/**
* pk_backend_get_internal:
*
* Must g_free() the return value. Returns NULL on error.
**/
gchar *
pk_backend_get_internal (PkBackend *backend, const gchar *key)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), NULL);
return NULL;
}
/**
* pk_backend_build_library_path:
**/
static gchar *
pk_backend_build_library_path (PkBackend *backend, const gchar *name)
{
gchar *path;
gchar *filename;
g_return_val_if_fail (PK_IS_BACKEND (backend), NULL);
g_return_val_if_fail (name != NULL, NULL);
filename = g_strdup_printf ("libpk_backend_%s.so", name);
#if PK_BUILD_LOCAL
/* prefer the local version */
path = g_build_filename ("..", "backends", name, ".libs", filename, NULL);
if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) {
pk_debug ("local backend not found '%s'", path);
g_free (path);
path = g_build_filename (LIBDIR, "packagekit-backend", filename, NULL);
}
#else
path = g_build_filename (LIBDIR, "packagekit-backend", filename, NULL);
#endif
g_free (filename);
pk_debug ("dlopening '%s'", path);
return path;
}
/**
* pk_backend_set_name:
**/
gboolean
pk_backend_set_name (PkBackend *backend, const gchar *backend_name)
{
GModule *handle;
gchar *path = NULL;
gboolean ret = TRUE;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend_name != NULL, FALSE);
/* have we already been set? */
if (backend->priv->name != NULL) {
pk_warning ("pk_backend_set_name called multiple times");
ret = FALSE;
goto out;
}
/* can we load it? */
pk_debug ("Trying to load : %s", backend_name);
path = pk_backend_build_library_path (backend, backend_name);
handle = g_module_open (path, 0);
if (handle == NULL) {
pk_warning ("opening module %s failed : %s", backend_name, g_module_error ());
ret = FALSE;
goto out;
}
/* is is correctly formed? */
if (!g_module_symbol (handle, "pk_backend_desc", (gpointer) &backend->desc)) {
g_module_close (handle);
pk_warning ("could not find description in plugin %s, not loading", backend_name);
ret = FALSE;
goto out;
}
/* save the backend name and handle */
g_free (backend->priv->name);
backend->priv->name = g_strdup (backend_name);
backend->priv->handle = handle;
out:
g_free (path);
return ret;
}
/**
* pk_backend_lock:
*
* Responsible for initialising the external backend object.
*
* Typically this will involve taking database locks for exclusive package access.
* This method should only be called from the engine, unless the backend object
* is used in self-check code, in which case the lock and unlock will have to
* be done manually.
**/
gboolean
pk_backend_lock (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->desc != NULL, FALSE);
if (backend->priv->locked) {
pk_warning ("already locked");
/* we don't return FALSE here, as the action didn't fail */
return TRUE;
}
if (backend->desc->initialize != NULL) {
backend->priv->during_initialize = TRUE;
backend->desc->initialize (backend);
backend->priv->during_initialize = FALSE;
}
backend->priv->locked = TRUE;
return TRUE;
}
/**
* pk_backend_unlock:
*
* Responsible for finalising the external backend object.
*
* Typically this will involve releasing database locks for any other access.
* This method should only be called from the engine, unless the backend object
* is used in self-check code, in which case it will have to be done manually.
**/
gboolean
pk_backend_unlock (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
if (backend->priv->locked == FALSE) {
pk_warning ("already unlocked");
/* we don't return FALSE here, as the action didn't fail */
return TRUE;
}
if (backend->desc == NULL) {
pk_warning ("not yet loaded backend, try pk_backend_lock()");
return FALSE;
}
if (backend->desc->destroy != NULL) {
backend->desc->destroy (backend);
}
backend->priv->locked = FALSE;
return TRUE;
}
/**
* pk_backend_get_name:
**/
gchar *
pk_backend_get_name (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), NULL);
return g_strdup (backend->priv->name);
}
/**
* pk_backend_emit_progress_changed:
**/
static gboolean
pk_backend_emit_progress_changed (PkBackend *backend)
{
guint percentage;
guint subpercentage;
guint elapsed;
guint remaining;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
percentage = backend->priv->last_percentage;
/* have not ever set any value? */
if (percentage == PK_BACKEND_PERCENTAGE_DEFAULT) {
percentage = PK_BACKEND_PERCENTAGE_INVALID;
}
subpercentage = backend->priv->last_subpercentage;
elapsed = pk_time_get_elapsed (backend->priv->time);
remaining = backend->priv->last_remaining;
pk_debug ("emit progress %i, %i, %i, %i",
percentage, subpercentage, elapsed, remaining);
g_signal_emit (backend, signals [PK_BACKEND_PROGRESS_CHANGED], 0,
percentage, subpercentage, elapsed, remaining);
return TRUE;
}
/**
* pk_backend_set_percentage:
**/
gboolean
pk_backend_set_percentage (PkBackend *backend, guint percentage)
{
guint remaining;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* set the same twice? */
if (backend->priv->last_percentage == percentage) {
pk_debug ("duplicate set of %i", percentage);
return FALSE;
}
/* check over */
if (percentage > PK_BACKEND_PERCENTAGE_INVALID) {
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"percentage value is invalid: %i", percentage);
return FALSE;
}
/* check under */
if (percentage < 100 &&
backend->priv->last_percentage < 100 &&
percentage < backend->priv->last_percentage) {
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"percentage value is going down to %i from %i",
percentage, backend->priv->last_percentage);
return FALSE;
}
/* save in case we need this from coldplug */
backend->priv->last_percentage = percentage;
/* only compute time if we have data */
if (percentage != PK_BACKEND_PERCENTAGE_INVALID) {
/* needed for time remaining calculation */
pk_time_add_data (backend->priv->time, percentage);
/* lets try this and print as debug */
remaining = pk_time_get_remaining (backend->priv->time);
pk_debug ("this will now take ~%i seconds", remaining);
#ifdef PK_IS_DEVELOPER
/* Until the predicted time is more sane... */
backend->priv->last_remaining = remaining;
#endif
}
/* emit the progress changed signal */
pk_backend_emit_progress_changed (backend);
return TRUE;
}
/**
* pk_backend_get_runtime:
*
* Returns time running in ms
*/
guint
pk_backend_get_runtime (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), 0);
g_return_val_if_fail (backend->priv->locked != FALSE, 0);
return pk_time_get_elapsed (backend->priv->time);
}
/**
* pk_backend_set_sub_percentage:
**/
gboolean
pk_backend_set_sub_percentage (PkBackend *backend, guint percentage)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* set the same twice? */
if (backend->priv->last_subpercentage == percentage) {
pk_debug ("duplicate set of %i", percentage);
return FALSE;
}
/* invalid number? */
if (percentage > 100 && percentage != PK_BACKEND_PERCENTAGE_INVALID) {
pk_debug ("invalid number %i", percentage);
return FALSE;
}
/* save in case we need this from coldplug */
backend->priv->last_subpercentage = percentage;
/* emit the progress changed signal */
pk_backend_emit_progress_changed (backend);
return TRUE;
}
/**
* pk_backend_no_percentage_updates:
**/
gboolean
pk_backend_no_percentage_updates (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* set the same twice? */
if (backend->priv->last_percentage == PK_BACKEND_PERCENTAGE_INVALID) {
pk_debug ("duplicate set of %i", PK_BACKEND_PERCENTAGE_INVALID);
return FALSE;
}
/* invalidate previous percentage */
backend->priv->last_percentage = PK_BACKEND_PERCENTAGE_INVALID;
/* emit the progress changed signal */
pk_backend_emit_progress_changed (backend);
return TRUE;
}
/**
* pk_backend_set_status:
**/
gboolean
pk_backend_set_status (PkBackend *backend, PkStatusEnum status)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* already this? */
if (backend->priv->status == status) {
pk_debug ("already set same status");
return TRUE;
}
/* have we already set an error? */
if (backend->priv->set_error && status != PK_STATUS_ENUM_FINISHED) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* backends don't do this */
if (status == PK_STATUS_ENUM_WAIT) {
pk_warning ("backend tried to WAIT, only the runner should set this value");
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"backends shouldn't use STATUS_WAIT");
return FALSE;
}
/* sanity check */
if (status == PK_STATUS_ENUM_SETUP && backend->priv->status != PK_STATUS_ENUM_WAIT) {
pk_warning ("backend tried to SETUP, but should be in WAIT");
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"Tried to SETUP when not in WAIT");
return FALSE;
}
/* do we have to enumate a running call? */
if (status != PK_STATUS_ENUM_RUNNING && status != PK_STATUS_ENUM_SETUP) {
if (backend->priv->status == PK_STATUS_ENUM_SETUP) {
pk_debug ("emiting status-changed running");
g_signal_emit (backend, signals [PK_BACKEND_STATUS_CHANGED], 0, PK_STATUS_ENUM_RUNNING);
}
}
backend->priv->status = status;
pk_debug ("emiting status-changed %s", pk_status_enum_to_text (status));
g_signal_emit (backend, signals [PK_BACKEND_STATUS_CHANGED], 0, status);
return TRUE;
}
/**
* pk_backend_get_status:
**/
PkStatusEnum
pk_backend_get_status (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), PK_STATUS_ENUM_UNKNOWN);
g_return_val_if_fail (backend->priv->locked != FALSE, PK_STATUS_ENUM_UNKNOWN);
return backend->priv->status;
}
/**
* pk_backend_package:
**/
gboolean
pk_backend_package (PkBackend *backend, PkInfoEnum info, const gchar *package_id, const gchar *summary)
{
gchar *summary_safe;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (package_id != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* we automatically set the transaction status for some infos */
if (info == PK_INFO_ENUM_DOWNLOADING) {
pk_backend_set_status (backend, PK_STATUS_ENUM_DOWNLOAD);
} else if (info == PK_INFO_ENUM_UPDATING) {
pk_backend_set_status (backend, PK_STATUS_ENUM_UPDATE);
} else if (info == PK_INFO_ENUM_INSTALLING) {
pk_backend_set_status (backend, PK_STATUS_ENUM_INSTALL);
} else if (info == PK_INFO_ENUM_REMOVING) {
pk_backend_set_status (backend, PK_STATUS_ENUM_REMOVE);
} else if (info == PK_INFO_ENUM_CLEANUP) {
pk_backend_set_status (backend, PK_STATUS_ENUM_CLEANUP);
} else if (info == PK_INFO_ENUM_OBSOLETING) {
pk_backend_set_status (backend, PK_STATUS_ENUM_OBSOLETE);
}
/* replace unsafe chars */
summary_safe = pk_strsafe (summary);
pk_debug ("emit package %s, %s, %s", pk_info_enum_to_text (info), package_id, summary_safe);
g_signal_emit (backend, signals [PK_BACKEND_PACKAGE], 0, info, package_id, summary_safe);
g_free (summary_safe);
return TRUE;
}
/**
* pk_backend_update_detail:
**/
gboolean
pk_backend_update_detail (PkBackend *backend, const gchar *package_id,
const gchar *updates, const gchar *obsoletes,
const gchar *vendor_url, const gchar *bugzilla_url,
const gchar *cve_url, PkRestartEnum restart,
const gchar *update_text)
{
gchar *update_text_safe;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (package_id != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* replace unsafe chars */
update_text_safe = pk_strsafe (update_text);
pk_debug ("emit update-detail %s, %s, %s, %s, %s, %s, %i, %s",
package_id, updates, obsoletes, vendor_url, bugzilla_url, cve_url, restart, update_text_safe);
g_signal_emit (backend, signals [PK_BACKEND_UPDATE_DETAIL], 0,
package_id, updates, obsoletes, vendor_url, bugzilla_url, cve_url, restart, update_text_safe);
g_free (update_text_safe);
return TRUE;
}
/**
* pk_backend_get_progress:
**/
gboolean
pk_backend_get_progress (PkBackend *backend,
guint *percentage, guint *subpercentage,
guint *elapsed, guint *remaining)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
*percentage = backend->priv->last_percentage;
/* have not ever set any value? */
if (*percentage == PK_BACKEND_PERCENTAGE_DEFAULT) {
*percentage = PK_BACKEND_PERCENTAGE_INVALID;
}
*subpercentage = backend->priv->last_subpercentage;
*elapsed = pk_time_get_elapsed (backend->priv->time);
*remaining = backend->priv->last_remaining;
return TRUE;
}
/**
* pk_backend_require_restart:
**/
gboolean
pk_backend_require_restart (PkBackend *backend, PkRestartEnum restart, const gchar *details)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
pk_debug ("emit require-restart %i, %s", restart, details);
g_signal_emit (backend, signals [PK_BACKEND_REQUIRE_RESTART], 0, restart, details);
return TRUE;
}
/**
* pk_backend_message:
**/
gboolean
pk_backend_message (PkBackend *backend, PkMessageEnum message, const gchar *format, ...)
{
va_list args;
gchar *buffer;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
va_start (args, format);
g_vasprintf (&buffer, format, args);
va_end (args);
pk_debug ("emit message %i, %s", message, buffer);
g_signal_emit (backend, signals [PK_BACKEND_MESSAGE], 0, message, buffer);
g_free (buffer);
return TRUE;
}
/**
* pk_backend_set_transaction_data:
**/
gboolean
pk_backend_set_transaction_data (PkBackend *backend, const gchar *data)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
pk_debug ("emit change-transaction-data %s", data);
g_signal_emit (backend, signals [PK_BACKEND_CHANGE_TRANSACTION_DATA], 0, data);
return TRUE;
}
/**
* pk_backend_description:
**/
gboolean
pk_backend_description (PkBackend *backend, const gchar *package_id,
const gchar *license, PkGroupEnum group,
const gchar *description, const gchar *url,
gulong size)
{
gchar *description_safe;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (package_id != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* replace unsafe chars */
description_safe = pk_strsafe (description);
pk_debug ("emit description %s, %s, %i, %s, %s, %ld",
package_id, license, group, description_safe, url,
size);
g_signal_emit (backend, signals [PK_BACKEND_DESCRIPTION], 0,
package_id, license, group, description_safe, url,
size);
g_free (description_safe);
return TRUE;
}
/**
* pk_backend_files:
**/
gboolean
pk_backend_files (PkBackend *backend, const gchar *package_id, const gchar *filelist)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (package_id != NULL, FALSE);
g_return_val_if_fail (filelist != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
pk_debug ("emit files %s, %s", package_id, filelist);
g_signal_emit (backend, signals [PK_BACKEND_FILES], 0,
package_id, filelist);
return TRUE;
}
/**
* pk_backend_repo_signature_required:
**/
gboolean
pk_backend_repo_signature_required (PkBackend *backend, const gchar *package_id,
const gchar *repository_name, const gchar *key_url,
const gchar *key_userid, const gchar *key_id, const gchar *key_fingerprint,
const gchar *key_timestamp, PkSigTypeEnum type)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (repository_name != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* check we don't do this more than once */
if (backend->priv->set_signature) {
pk_warning ("already asked for a signature, cannot process");
return FALSE;
}
backend->priv->set_signature = TRUE;
pk_debug ("emit repo-signature-required %s, %s, %s, %s, %s, %s, %s, %i",
package_id, repository_name, key_url, key_userid, key_id,
key_fingerprint, key_timestamp, type);
g_signal_emit (backend, signals [PK_BACKEND_REPO_SIGNATURE_REQUIRED], 0,
package_id, repository_name, key_url, key_userid, key_id,
key_fingerprint, key_timestamp, type);
return TRUE;
}
/**
* pk_backend_repo_detail:
**/
gboolean
pk_backend_repo_detail (PkBackend *backend, const gchar *repo_id,
const gchar *description, gboolean enabled)
{
gchar *description_safe;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (repo_id != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* replace unsafe chars */
description_safe = pk_strsafe (description);
pk_debug ("emit repo-detail %s, %s, %i", repo_id, description_safe, enabled);
g_signal_emit (backend, signals [PK_BACKEND_REPO_DETAIL], 0, repo_id, description, enabled);
g_free (description_safe);
return TRUE;
}
/**
* pk_backend_error_timeout_delay_cb:
*
* We have to call Finished() within PK_BACKEND_FINISHED_ERROR_TIMEOUT of ErrorCode(), enforce this.
**/
static gboolean
pk_backend_error_timeout_delay_cb (gpointer data)
{
PkBackend *backend = PK_BACKEND (data);
PkMessageEnum message;
const gchar *buffer;
/* check we have not already finished */
if (backend->priv->finished) {
pk_warning ("consistency error");
return FALSE;
}
/* warn the backend developer that they've done something worng
* - we can't use pk_backend_message here as we have already set
* backend->priv->set_error to TRUE and hence the message would be ignored */
message = PK_MESSAGE_ENUM_DAEMON;
buffer = "ErrorCode() has to be followed with Finished()!";
pk_debug ("emit message %i, %s", message, buffer);
g_signal_emit (backend, signals [PK_BACKEND_MESSAGE], 0, message, buffer);
pk_backend_finished (backend);
return FALSE;
}
/**
* pk_backend_error_code:
**/
gboolean
pk_backend_error_code (PkBackend *backend, PkErrorCodeEnum code, const gchar *format, ...)
{
va_list args;
gchar *buffer;
gboolean ret = TRUE;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
va_start (args, format);
buffer = g_strdup_vprintf (format, args);
va_end (args);
/* check we are not doing Init() */
if (backend->priv->during_initialize) {
pk_warning ("set during init: %s", buffer);
ret = FALSE;
goto out;
}
/* did we set a duplicate error? */
if (backend->priv->set_error) {
pk_warning ("More than one error emitted! You tried to set '%s'", buffer);
ret = FALSE;
goto out;
}
backend->priv->set_error = TRUE;
/* we only allow a short time to send finished after error_code */
backend->priv->signal_error_timeout = g_timeout_add (PK_BACKEND_FINISHED_ERROR_TIMEOUT,
pk_backend_error_timeout_delay_cb, backend);
/* we mark any transaction with errors as failed */
pk_backend_set_exit_code (backend, PK_EXIT_ENUM_FAILED);
pk_debug ("emit error-code %i, %s", code, buffer);
g_signal_emit (backend, signals [PK_BACKEND_ERROR_CODE], 0, code, buffer);
out:
g_free (buffer);
return ret;
}
/**
* pk_backend_set_allow_cancel:
**/
gboolean
pk_backend_set_allow_cancel (PkBackend *backend, gboolean allow_cancel)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->desc != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* have we already set an error? */
if (backend->priv->set_error) {
pk_warning ("already set error, cannot process");
return FALSE;
}
/* remove or add the hal inhibit */
if (allow_cancel) {
pk_inhibit_remove (backend->priv->inhibit, backend);
} else {
pk_inhibit_add (backend->priv->inhibit, backend);
}
/* can we do the action? */
if (backend->desc->cancel != NULL) {
backend->priv->allow_cancel = allow_cancel;
pk_debug ("emit allow-cancel %i", allow_cancel);
g_signal_emit (backend, signals [PK_BACKEND_ALLOW_CANCEL], 0, allow_cancel);
}
return TRUE;
}
/**
* pk_backend_get_allow_cancel:
**/
gboolean
pk_backend_get_allow_cancel (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
return backend->priv->allow_cancel;
}
/**
* pk_backend_set_role:
**/
gboolean
pk_backend_set_role (PkBackend *backend, PkRoleEnum role)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* Should only be called once... */
if (backend->priv->role != PK_ROLE_ENUM_UNKNOWN) {
pk_warning ("cannot set role more than once, already %s",
pk_role_enum_to_text (backend->priv->role));
return FALSE;
}
/* reset the timer */
pk_time_reset (backend->priv->time);
pk_debug ("setting role to %s", pk_role_enum_to_text (role));
backend->priv->role = role;
backend->priv->status = PK_STATUS_ENUM_WAIT;
return TRUE;
}
/**
* pk_backend_get_role:
**/
PkRoleEnum
pk_backend_get_role (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), PK_ROLE_ENUM_UNKNOWN);
g_return_val_if_fail (backend->priv->locked != FALSE, PK_ROLE_ENUM_UNKNOWN);
return backend->priv->role;
}
/**
* pk_backend_set_exit_code:
*
* Should only be used internally, or from PkRunner when setting CANCELLED.
**/
gboolean
pk_backend_set_exit_code (PkBackend *backend, PkExitEnum exit)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), PK_ROLE_ENUM_UNKNOWN);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
if (backend->priv->exit != PK_EXIT_ENUM_UNKNOWN) {
pk_warning ("already set exit status: old=%s, new=%s",
pk_exit_enum_to_text (backend->priv->exit),
pk_exit_enum_to_text (exit));
return FALSE;
}
/* new value */
backend->priv->exit = exit;
return TRUE;
}
/**
* pk_backend_finished_delay:
*
* We can call into this function if we *know* it's safe.
**/
static gboolean
pk_backend_finished_delay (gpointer data)
{
PkBackend *backend = PK_BACKEND (data);
/* this wasn't set otherwise, assume success */
if (backend->priv->exit == PK_EXIT_ENUM_UNKNOWN) {
pk_backend_set_exit_code (backend, PK_EXIT_ENUM_SUCCESS);
}
pk_debug ("emit finished %i", backend->priv->exit);
g_signal_emit (backend, signals [PK_BACKEND_FINISHED], 0, backend->priv->exit);
backend->priv->signal_finished = 0;
return FALSE;
}
/**
* pk_backend_finished:
**/
gboolean
pk_backend_finished (PkBackend *backend)
{
const gchar *role_text;
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
/* check we are not doing Init() */
if (backend->priv->during_initialize) {
pk_warning ("finished during init");
return FALSE;
}
/* safe to check now */
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* find out what we just did */
role_text = pk_role_enum_to_text (backend->priv->role);
pk_debug ("finished role %s", role_text);
/* are we trying to finish in init? */
if (backend->priv->during_initialize) {
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"You can't call pk_backend_finished in backend_initialize!");
return FALSE;
}
/* check we have not already finished */
if (backend->priv->finished) {
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"Backends cannot request Finished more than once!");
return FALSE;
}
/* if we set an error code notifier, clear */
if (backend->priv->signal_error_timeout != 0) {
g_source_remove (backend->priv->signal_error_timeout);
backend->priv->signal_error_timeout = 0;
}
/* check we sent at least one status calls */
if (backend->priv->set_error == FALSE &&
backend->priv->status == PK_STATUS_ENUM_SETUP) {
pk_backend_message (backend, PK_MESSAGE_ENUM_DAEMON,
"Backends should send status <value> signals for %s!\n"
"If you are:\n"
"* Calling out to external tools, the compiled backend "
"should call pk_backend_set_status() manually.\n"
"* Using a scripted backend with dumb commands then "
"this should be set at the start of the runtime call\n"
" - see helpers/yumBackend.py:self.status()\n"
"* Using a scripted backend with clever commands then a "
" callback should use map values into status enums\n"
" - see helpers/yumBackend.py:self.state_actions", role_text);
pk_warning ("GUI will remain unchanged!");
}
/* make any UI insensitive */
pk_backend_set_allow_cancel (backend, FALSE);
/* mark as finished for the UI that might only be watching status */
pk_backend_set_status (backend, PK_STATUS_ENUM_FINISHED);
/* we can't ever be re-used */
backend->priv->finished = TRUE;
/* remove any inhibit */
pk_inhibit_remove (backend->priv->inhibit, backend);
/* we have to run this idle as the command may finish before the transaction
* has been sent to the client. I love async... */
pk_debug ("adding finished %p to timeout loop", backend);
backend->priv->signal_finished = g_timeout_add (PK_BACKEND_FINISHED_TIMEOUT_GRACE, pk_backend_finished_delay, backend);
return TRUE;
}
/**
* pk_backend_not_implemented_yet:
**/
gboolean
pk_backend_not_implemented_yet (PkBackend *backend, const gchar *method)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (method != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
/* this function is only valid when we have a running transaction */
if (backend->priv->c_tid != NULL) {
pk_error ("only valid when we have a running transaction");
return FALSE;
}
pk_backend_error_code (backend, PK_ERROR_ENUM_NOT_SUPPORTED, "the method '%s' is not implemented yet", method);
/* don't wait, do this now */
pk_backend_finished_delay (backend);
return TRUE;
}
/**
* pk_backend_get_backend_detail:
*/
gboolean
pk_backend_get_backend_detail (PkBackend *backend, gchar **name, gchar **author)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->desc != NULL, FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
if (name != NULL && backend->desc->description != NULL) {
*name = g_strdup (backend->desc->description);
}
if (author != NULL && backend->desc->author != NULL) {
*author = g_strdup (backend->desc->author);
}
return TRUE;
}
/**
* pk_backend_get_current_tid:
*/
const gchar *
pk_backend_get_current_tid (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), NULL);
g_return_val_if_fail (backend->priv->locked != FALSE, NULL);
return backend->priv->c_tid;
}
/**
* pk_backend_set_current_tid:
*/
gboolean
pk_backend_set_current_tid (PkBackend *backend, const gchar *tid)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
pk_debug ("setting backend tid as %s", tid);
g_free (backend->priv->c_tid);
backend->priv->c_tid = g_strdup (tid);
return TRUE;
}
/**
* pk_backend_finalize:
**/
static void
pk_backend_finalize (GObject *object)
{
PkBackend *backend;
g_return_if_fail (PK_IS_BACKEND (object));
backend = PK_BACKEND (object);
pk_debug ("backend finalise");
g_object_unref (backend->priv->time);
g_object_unref (backend->priv->inhibit);
/* do finish now, as we might be unreffing quickly */
if (backend->priv->signal_finished != 0) {
g_source_remove (backend->priv->signal_finished);
pk_backend_finished_delay (backend);
}
/* if we set an error code notifier, clear */
if (backend->priv->signal_error_timeout != 0) {
g_source_remove (backend->priv->signal_error_timeout);
}
g_free (backend->priv->name);
g_free (backend->priv->c_tid);
if (backend->priv->handle != NULL) {
g_module_close (backend->priv->handle);
}
pk_debug ("parent_class->finalize");
G_OBJECT_CLASS (pk_backend_parent_class)->finalize (object);
}
/**
* pk_backend_class_init:
**/
static void
pk_backend_class_init (PkBackendClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pk_backend_finalize;
signals [PK_BACKEND_STATUS_CHANGED] =
g_signal_new ("status-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals [PK_BACKEND_PROGRESS_CHANGED] =
g_signal_new ("progress-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__UINT_UINT_UINT_UINT,
G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
signals [PK_BACKEND_PACKAGE] =
g_signal_new ("package",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__UINT_STRING_STRING,
G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
signals [PK_BACKEND_UPDATE_DETAIL] =
g_signal_new ("update-detail",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__STRING_STRING_STRING_STRING_STRING_STRING_UINT_STRING,
G_TYPE_NONE, 8, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
signals [PK_BACKEND_REQUIRE_RESTART] =
g_signal_new ("require-restart",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__UINT_STRING,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
signals [PK_BACKEND_MESSAGE] =
g_signal_new ("message",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__UINT_STRING,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
signals [PK_BACKEND_CHANGE_TRANSACTION_DATA] =
g_signal_new ("change-transaction-data",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals [PK_BACKEND_DESCRIPTION] =
g_signal_new ("description",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__STRING_STRING_UINT_STRING_STRING_UINT64,
G_TYPE_NONE, 6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_UINT64);
signals [PK_BACKEND_FILES] =
g_signal_new ("files",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__STRING_STRING,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
signals [PK_BACKEND_ERROR_CODE] =
g_signal_new ("error-code",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__UINT_STRING,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
signals [PK_BACKEND_REPO_SIGNATURE_REQUIRED] =
g_signal_new ("repo-signature-required",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__STRING_STRING_STRING_STRING_STRING_STRING_STRING_UINT,
G_TYPE_NONE, 8, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
signals [PK_BACKEND_FINISHED] =
g_signal_new ("finished",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals [PK_BACKEND_ALLOW_CANCEL] =
g_signal_new ("allow-cancel",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
signals [PK_BACKEND_REPO_DETAIL] =
g_signal_new ("repo-detail",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, pk_marshal_VOID__STRING_STRING_BOOL,
G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
g_type_class_add_private (klass, sizeof (PkBackendPrivate));
}
/**
* pk_backend_reset:
**/
gboolean
pk_backend_reset (PkBackend *backend)
{
g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
backend->priv->set_error = FALSE;
backend->priv->set_signature = FALSE;
backend->priv->allow_cancel = FALSE;
backend->priv->finished = FALSE;
backend->priv->status = PK_STATUS_ENUM_UNKNOWN;
backend->priv->exit = PK_EXIT_ENUM_UNKNOWN;
backend->priv->role = PK_ROLE_ENUM_UNKNOWN;
backend->priv->last_remaining = 0;
backend->priv->last_percentage = PK_BACKEND_PERCENTAGE_DEFAULT;
backend->priv->last_subpercentage = PK_BACKEND_PERCENTAGE_INVALID;
pk_time_reset (backend->priv->time);
return TRUE;
}
/**
* pk_backend_init:
**/
static void
pk_backend_init (PkBackend *backend)
{
backend->priv = PK_BACKEND_GET_PRIVATE (backend);
backend->priv->handle = NULL;
backend->priv->name = NULL;
backend->priv->c_tid = NULL;
backend->priv->locked = FALSE;
backend->priv->signal_finished = 0;
backend->priv->signal_error_timeout = 0;
backend->priv->during_initialize = FALSE;
backend->priv->time = pk_time_new ();
backend->priv->inhibit = pk_inhibit_new ();
pk_backend_reset (backend);
}
/**
* pk_backend_new:
* Return value: A new backend class backend.
**/
PkBackend *
pk_backend_new (void)
{
pk_debug ("new object");
if (pk_backend_object != NULL) {
g_object_ref (pk_backend_object);
} else {
pk_backend_object = g_object_new (PK_TYPE_BACKEND, NULL);
g_object_add_weak_pointer (pk_backend_object, &pk_backend_object);
}
return PK_BACKEND (pk_backend_object);
}
/***************************************************************************
*** MAKE CHECK TESTS ***
***************************************************************************/
#ifdef PK_BUILD_TESTS
#include <libselftest.h>
static guint number_messages = 0;
/**
* pk_backend_test_message_cb:
**/
static void
pk_backend_test_message_cb (PkBackend *backend, PkMessageEnum message, const gchar *details, gpointer data)
{
pk_debug ("details=%s", details);
number_messages++;
}
/**
* pk_backend_test_finished_cb:
**/
static void
pk_backend_test_finished_cb (PkBackend *backend, PkExitEnum exit, LibSelfTest *test)
{
libst_loopquit (test);
}
void
libst_backend (LibSelfTest *test)
{
PkBackend *backend;
gchar *text;
gboolean ret;
if (libst_start (test, "PkBackend", CLASS_AUTO) == FALSE) {
return;
}
/************************************************************/
libst_title (test, "get an backend");
backend = pk_backend_new ();
if (backend != NULL) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
g_signal_connect (backend, "message", G_CALLBACK (pk_backend_test_message_cb), NULL);
g_signal_connect (backend, "finished", G_CALLBACK (pk_backend_test_finished_cb), test);
/************************************************************/
libst_title (test, "get backend name");
text = pk_backend_get_name (backend);
if (text == NULL) {
libst_success (test, NULL);
} else {
libst_failed (test, "invalid name %s", text);
}
g_free (text);
/************************************************************/
libst_title (test, "load an invalid backend");
ret = pk_backend_set_name (backend, "invalid");
if (ret == FALSE) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "try to load a valid backend");
ret = pk_backend_set_name (backend, "dummy");
if (ret) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "load an valid backend again");
ret = pk_backend_set_name (backend, "dummy");
if (ret == FALSE) {
libst_success (test, NULL);
} else {
libst_failed (test, "loaded twice");
}
/************************************************************/
libst_title (test, "lock an valid backend");
ret = pk_backend_lock (backend);
if (ret) {
libst_success (test, NULL);
} else {
libst_failed (test, "failed to lock");
}
/************************************************************/
libst_title (test, "lock a backend again");
ret = pk_backend_lock (backend);
if (ret) {
libst_success (test, NULL);
} else {
libst_failed (test, "locked twice should succeed");
}
/************************************************************/
libst_title (test, "check we are out of init");
if (backend->priv->during_initialize == FALSE) {
libst_success (test, NULL);
} else {
libst_failed (test, "not out of init");
}
/************************************************************/
libst_title (test, "get backend name");
text = pk_backend_get_name (backend);
if (pk_strequal(text, "dummy")) {
libst_success (test, NULL);
} else {
libst_failed (test, "invalid name %s", text);
}
g_free (text);
/************************************************************/
libst_title (test, "unlock an valid backend");
ret = pk_backend_unlock (backend);
if (ret) {
libst_success (test, NULL);
} else {
libst_failed (test, "failed to unlock");
}
/************************************************************/
libst_title (test, "unlock an valid backend again");
ret = pk_backend_unlock (backend);
if (ret) {
libst_success (test, NULL);
} else {
libst_failed (test, "unlocked twice, should succeed");
}
/************************************************************/
libst_title (test, "check we are not finished");
if (backend->priv->finished == FALSE) {
libst_success (test, NULL);
} else {
libst_failed (test, "we did not clear finish!");
}
/************************************************************/
libst_title (test, "check we have no error");
if (backend->priv->set_error == FALSE) {
libst_success (test, NULL);
} else {
libst_failed (test, "an error has already been set");
}
/************************************************************/
libst_title (test, "lock again");
ret = pk_backend_lock (backend);
if (ret) {
libst_success (test, NULL);
} else {
libst_failed (test, "failed to unlock");
}
/************************************************************/
libst_title (test, "check we enforce finished after error_code");
pk_backend_error_code (backend, PK_ERROR_ENUM_GPG_FAILURE, "test error");
/* wait for finished */
libst_loopwait (test, PK_BACKEND_FINISHED_ERROR_TIMEOUT + 200);
libst_loopcheck (test);
if (number_messages == 1) {
libst_success (test, NULL);
} else {
libst_failed (test, "we messaged %i times!", number_messages);
}
/* reset */
pk_backend_reset (backend);
number_messages = 0;
/************************************************************/
libst_title (test, "check we enforce finished after two error_codes");
pk_backend_error_code (backend, PK_ERROR_ENUM_GPG_FAILURE, "test error1");
pk_backend_error_code (backend, PK_ERROR_ENUM_GPG_FAILURE, "test error2");
/* wait for finished */
libst_loopwait (test, PK_BACKEND_FINISHED_ERROR_TIMEOUT + 100);
libst_loopcheck (test);
if (number_messages == 1) {
libst_success (test, NULL);
} else {
libst_failed (test, "we messaged %i times!", number_messages);
}
g_object_unref (backend);
libst_end (test);
}
#endif