#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib/gi18n.h>
#include <packagekit-glib2/pk-common.h>
#include "pk-shared.h"
#include "pk-transaction.h"
#include "pk-transaction-private.h"
#include "pk-scheduler.h"
static void pk_scheduler_finalize (GObject *object);
#define PK_SCHEDULER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_SCHEDULER, PkSchedulerPrivate))
#define PK_TRANSACTION_WEDGE_CHECK 10
#define PK_TRANSACTION_KEEP_FINISHED_TIMOUT 5
#define PK_SCHEDULER_MAX_LOCK_RETRIES 4
#define PK_SCHEDULER_CREATE_COMMIT_TIMEOUT 300
#define PK_SCHEDULER_SIMULTANEOUS_TRANSACTIONS_FOR_UID 500
struct PkSchedulerPrivate
{
GPtrArray *array;
guint unwedge_id;
GKeyFile *conf;
PkBackend *backend;
GDBusNodeInfo *introspection;
};
typedef struct {
PkTransaction *transaction;
PkScheduler *scheduler;
gchar *tid;
guint remove_id;
guint idle_id;
guint commit_id;
gulong finished_id;
gulong state_changed_id;
gulong allow_cancel_changed_id;
guint uid;
guint tries;
} PkSchedulerItem;
enum {
PK_SCHEDULER_CHANGED,
PK_SCHEDULER_LAST_SIGNAL
};
static guint signals [PK_SCHEDULER_LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (PkScheduler, pk_scheduler, G_TYPE_OBJECT)
static PkSchedulerItem *
pk_scheduler_get_from_tid (PkScheduler *scheduler, const gchar *tid)
{
guint i;
GPtrArray *array;
PkSchedulerItem *item;
const gchar *tmptid;
g_return_val_if_fail (scheduler != NULL, NULL);
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), NULL);
array = scheduler->priv->array;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
tmptid = pk_transaction_get_tid (item->transaction);
if (g_strcmp0 (tmptid, tid) == 0)
return item;
}
return NULL;
}
PkTransaction *
pk_scheduler_get_transaction (PkScheduler *scheduler, const gchar *tid)
{
PkSchedulerItem *item;
g_return_val_if_fail (pk_is_thread_default (), NULL);
item = pk_scheduler_get_from_tid (scheduler, tid);
if (item == NULL)
return NULL;
return item->transaction;
}
gboolean
pk_scheduler_role_present (PkScheduler *scheduler, PkRoleEnum role)
{
guint i;
GPtrArray *array;
PkRoleEnum role_temp;
PkSchedulerItem *item;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
g_return_val_if_fail (pk_is_thread_default (), FALSE);
array = scheduler->priv->array;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
if (item->transaction == NULL)
continue;
if (pk_transaction_get_state (item->transaction) == PK_TRANSACTION_STATE_FINISHED)
continue;
role_temp = pk_transaction_get_role (item->transaction);
if (role_temp == role)
return TRUE;
}
return FALSE;
}
static void
pk_scheduler_item_free (PkSchedulerItem *item)
{
g_return_if_fail (item != NULL);
if (item->finished_id != 0)
g_signal_handler_disconnect (item->transaction, item->finished_id);
if (item->state_changed_id != 0)
g_signal_handler_disconnect (item->transaction, item->state_changed_id);
if (item->allow_cancel_changed_id != 0)
g_signal_handler_disconnect (item->transaction, item->allow_cancel_changed_id);
g_object_unref (item->transaction);
if (item->commit_id != 0)
g_source_remove (item->commit_id);
if (item->idle_id != 0)
g_source_remove (item->idle_id);
if (item->remove_id != 0)
g_source_remove (item->remove_id);
g_object_unref (item->scheduler);
g_free (item->tid);
g_free (item);
}
static void
pk_scheduler_item_free_cb (PkSchedulerItem *item, gpointer user_data)
{
pk_scheduler_item_free (item);
}
static gboolean
pk_scheduler_remove_internal (PkScheduler *scheduler, PkSchedulerItem *item)
{
gboolean ret;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
g_return_val_if_fail (item != NULL, FALSE);
ret = g_ptr_array_remove (scheduler->priv->array, item);
if (!ret) {
g_warning ("could not remove %p as not present in list", item);
return FALSE;
}
pk_scheduler_item_free (item);
return TRUE;
}
gboolean
pk_scheduler_remove (PkScheduler *scheduler, const gchar *tid)
{
PkSchedulerItem *item;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
g_return_val_if_fail (tid != NULL, FALSE);
g_return_val_if_fail (pk_is_thread_default (), FALSE);
item = pk_scheduler_get_from_tid (scheduler, tid);
if (item == NULL) {
g_warning ("could not get item");
return FALSE;
}
if (pk_transaction_get_state (item->transaction) == PK_TRANSACTION_STATE_FINISHED) {
g_debug ("already finished, so waiting to timeout");
return FALSE;
}
if (item->remove_id != 0) {
g_source_remove (item->remove_id);
item->remove_id = 0;
}
if (pk_transaction_get_state (item->transaction) == PK_TRANSACTION_STATE_RUNNING) {
if (item->idle_id == 0) {
g_warning ("already running, but no idle_id");
return FALSE;
}
g_debug ("cancelling the callback to the 'lost' transaction");
g_source_remove (item->idle_id);
item->idle_id = 0;
}
return pk_scheduler_remove_internal (scheduler, item);
}
static gboolean
pk_scheduler_remove_item_cb (gpointer user_data)
{
PkSchedulerItem *item = (PkSchedulerItem *) user_data;
g_debug ("transaction %s completed, removing", item->tid);
pk_scheduler_remove_internal (item->scheduler, item);
return FALSE;
}
static gboolean
pk_scheduler_run_idle_cb (PkSchedulerItem *item)
{
gboolean ret;
pk_transaction_set_backend (item->transaction,
item->scheduler->priv->backend);
ret = pk_transaction_run (item->transaction);
if (!ret)
g_error ("failed to run transaction (fatal)");
item->idle_id = 0;
return FALSE;
}
static void
pk_scheduler_run_item (PkScheduler *scheduler, PkSchedulerItem *item)
{
pk_transaction_set_state (item->transaction, PK_TRANSACTION_STATE_RUNNING);
item->idle_id = g_idle_add ((GSourceFunc) pk_scheduler_run_idle_cb, item);
g_source_set_name_by_id (item->idle_id, "[PkScheduler] run");
}
static GPtrArray *
pk_scheduler_get_active_transactions (PkScheduler *scheduler)
{
guint i;
GPtrArray *array;
GPtrArray *res;
PkSchedulerItem *item;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), NULL);
res = g_ptr_array_new ();
array = scheduler->priv->array;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
if (pk_transaction_get_state (item->transaction) == PK_TRANSACTION_STATE_RUNNING)
g_ptr_array_add (res, item);
}
return res;
}
static guint
pk_scheduler_get_exclusive_running (PkScheduler *scheduler)
{
PkSchedulerItem *item = NULL;
guint exclusive_running = 0;
guint i;
g_autoptr(GPtrArray) array = NULL;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
array = pk_scheduler_get_active_transactions (scheduler);
if (array->len == 0)
return 0;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
if (pk_transaction_is_exclusive (item->transaction)) {
exclusive_running++;
}
}
return exclusive_running;
}
static gboolean
pk_scheduler_get_background_running (PkScheduler *scheduler)
{
PkSchedulerItem *item = NULL;
guint i;
g_autoptr(GPtrArray) array = NULL;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
array = pk_scheduler_get_active_transactions (scheduler);
if (array->len == 0)
return FALSE;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
if (pk_transaction_get_background (item->transaction))
return TRUE;
}
return FALSE;
}
static PkSchedulerItem *
pk_scheduler_get_next_item (PkScheduler *scheduler)
{
PkSchedulerItem *item = NULL;
GPtrArray *array;
guint i;
PkTransactionState state;
gboolean exclusive_running;
array = scheduler->priv->array;
exclusive_running = pk_scheduler_get_exclusive_running (scheduler) > 0;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
state = pk_transaction_get_state (item->transaction);
if ((state == PK_TRANSACTION_STATE_READY) && (!pk_transaction_get_background (item->transaction))) {
if (pk_transaction_is_exclusive (item->transaction)) {
if (!exclusive_running)
goto out;
} else {
goto out;
}
}
}
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
state = pk_transaction_get_state (item->transaction);
if (state == PK_TRANSACTION_STATE_READY) {
if (pk_transaction_is_exclusive (item->transaction)) {
if (!exclusive_running)
goto out;
} else {
goto out;
}
}
}
item = NULL;
out:
return item;
}
static void
pk_scheduler_commit (PkScheduler *scheduler, const gchar *tid)
{
PkSchedulerItem *item;
g_return_if_fail (PK_IS_SCHEDULER (scheduler));
g_return_if_fail (tid != NULL);
item = pk_scheduler_get_from_tid (scheduler, tid);
if (item == NULL) {
g_warning ("could not get transaction: %s", tid);
return;
}
if (!pk_backend_supports_parallelization (scheduler->priv->backend))
pk_transaction_make_exclusive (item->transaction);
if (item->commit_id != 0) {
g_source_remove (item->commit_id);
item->commit_id = 0;
}
g_signal_emit (scheduler, signals [PK_SCHEDULER_CHANGED], 0);
if (!pk_transaction_get_background (item->transaction) &&
pk_scheduler_get_background_running (scheduler)) {
g_debug ("cancelling running background transactions and instead running %s",
item->tid);
pk_scheduler_cancel_background (scheduler);
}
if (pk_transaction_is_exclusive (item->transaction) == FALSE ||
pk_scheduler_get_exclusive_running (scheduler) == 0)
pk_scheduler_run_item (scheduler, item);
}
static void
pk_scheduler_transaction_allow_cancel_changed_cb (PkTransaction *transaction,
gboolean allow_cancel,
PkScheduler *scheduler)
{
g_signal_emit (scheduler, signals [PK_SCHEDULER_CHANGED], 0);
}
static void
pk_scheduler_transaction_state_changed_cb (PkTransaction *transaction,
PkTransactionState state,
PkScheduler *scheduler)
{
if (state == PK_TRANSACTION_STATE_ERROR) {
pk_scheduler_remove (scheduler, pk_transaction_get_tid (transaction));
return;
}
if (state == PK_TRANSACTION_STATE_READY) {
pk_scheduler_commit (scheduler, pk_transaction_get_tid (transaction));
return;
}
}
static void
pk_scheduler_transaction_finished_cb (PkTransaction *transaction,
PkScheduler *scheduler)
{
PkSchedulerItem *item;
PkTransactionState state;
PkBackendJob *job;
const gchar *tid;
g_return_if_fail (PK_IS_SCHEDULER (scheduler));
tid = pk_transaction_get_tid (transaction);
item = pk_scheduler_get_from_tid (scheduler, tid);
if (item == NULL)
g_error ("no transaction list item '%s' found!", tid);
state = pk_transaction_get_state (item->transaction);
if (state == PK_TRANSACTION_STATE_FINISHED) {
g_warning ("transaction %s finished twice!", item->tid);
return;
}
if (pk_transaction_is_finished_with_lock_required (item->transaction)) {
pk_transaction_reset_after_lock_error (item->transaction);
item->tries++;
g_debug ("transaction finished and requires lock now, attempt %i", item->tries);
if (item->tries > PK_SCHEDULER_MAX_LOCK_RETRIES) {
job = pk_transaction_get_backend_job (item->transaction);
pk_backend_job_error_code (job, PK_ERROR_ENUM_CANNOT_GET_LOCK,
"Unable to lock package database! There is probably another application using it already.");
pk_backend_job_finished (job);
return;
}
} else {
if (item->commit_id != 0) {
g_source_remove (item->commit_id);
item->commit_id = 0;
}
pk_transaction_set_state (item->transaction, PK_TRANSACTION_STATE_FINISHED);
item->remove_id = g_timeout_add_seconds (PK_TRANSACTION_KEEP_FINISHED_TIMOUT,
pk_scheduler_remove_item_cb,
item);
g_source_set_name_by_id (item->remove_id, "[PkScheduler] remove");
}
item = pk_scheduler_get_next_item (scheduler);
if (item != NULL) {
g_debug ("running %s as previous one finished", item->tid);
pk_scheduler_run_item (scheduler, item);
}
g_signal_emit (scheduler, signals [PK_SCHEDULER_CHANGED], 0);
}
static gboolean
pk_scheduler_no_commit_cb (gpointer user_data)
{
PkSchedulerItem *item = (PkSchedulerItem *) user_data;
g_warning ("ID %s was not committed in time!", item->tid);
pk_transaction_cancel_bg (item->transaction);
pk_scheduler_remove_internal (item->scheduler, item);
return FALSE;
}
static guint
pk_scheduler_get_number_transactions_for_uid (PkScheduler *scheduler, guint uid)
{
guint i;
GPtrArray *array;
PkSchedulerItem *item;
guint count = 0;
array = scheduler->priv->array;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
if (item->uid == uid)
count++;
}
return count;
}
gboolean
pk_scheduler_create (PkScheduler *scheduler,
const gchar *tid,
const gchar *sender,
GError **error)
{
guint count;
gboolean ret = FALSE;
PkSchedulerItem *item;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
g_return_val_if_fail (tid != NULL, FALSE);
g_return_val_if_fail (pk_is_thread_default (), FALSE);
item = pk_scheduler_get_from_tid (scheduler, tid);
if (item != NULL) {
g_set_error (error, 1, 0, "already added %s to list", tid);
return FALSE;
}
item = g_new0 (PkSchedulerItem, 1);
item->scheduler = g_object_ref (scheduler);
item->tid = g_strdup (tid);
item->transaction = pk_transaction_new (scheduler->priv->conf,
scheduler->priv->introspection);
item->finished_id =
g_signal_connect_after (item->transaction, "finished",
G_CALLBACK (pk_scheduler_transaction_finished_cb),
scheduler);
item->state_changed_id =
g_signal_connect_after (item->transaction, "state-changed",
G_CALLBACK (pk_scheduler_transaction_state_changed_cb),
scheduler);
item->allow_cancel_changed_id =
g_signal_connect_after (item->transaction, "allow-cancel-changed",
G_CALLBACK (pk_scheduler_transaction_allow_cancel_changed_cb),
scheduler);
pk_transaction_set_state (item->transaction, PK_TRANSACTION_STATE_NEW);
ret = pk_transaction_set_tid (item->transaction, item->tid);
if (!ret) {
g_set_error (error, 1, 0, "failed to set TID: %s", tid);
return FALSE;
}
ret = pk_transaction_set_sender (item->transaction, sender);
if (!ret) {
g_set_error (error, 1, 0, "failed to set sender: %s", tid);
return FALSE;
}
if (scheduler->priv->backend != NULL) {
pk_transaction_set_backend (item->transaction,
scheduler->priv->backend);
}
item->uid = pk_transaction_get_uid (item->transaction);
count = pk_scheduler_get_number_transactions_for_uid (scheduler, item->uid);
if (count > PK_SCHEDULER_SIMULTANEOUS_TRANSACTIONS_FOR_UID) {
g_set_error (error, 1, 0,
"failed to allocate %s as uid %i already has "
"%i transactions in progress",
tid, item->uid, count);
pk_scheduler_item_free (item);
return FALSE;
}
item->commit_id = g_timeout_add_seconds (PK_SCHEDULER_CREATE_COMMIT_TIMEOUT,
pk_scheduler_no_commit_cb,
item);
g_source_set_name_by_id (item->commit_id, "[PkScheduler] commit");
g_debug ("adding transaction %p", item->transaction);
g_ptr_array_add (scheduler->priv->array, item);
return TRUE;
}
gboolean
pk_scheduler_get_locked (PkScheduler *scheduler)
{
PkBackendJob *job;
PkSchedulerItem *item;
guint i;
g_autoptr(GPtrArray) array = NULL;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
g_return_val_if_fail (pk_is_thread_default (), FALSE);
array = pk_scheduler_get_active_transactions (scheduler);
for (i = 0; i < array->len; i++) {
item = g_ptr_array_index (array, i);
job = pk_transaction_get_backend_job (item->transaction);
if (job == NULL)
continue;
if (pk_backend_job_get_locked (job))
return TRUE;
}
return FALSE;
}
gboolean
pk_scheduler_get_inhibited (PkScheduler *scheduler)
{
PkBackendJob *job;
PkSchedulerItem *item;
guint i;
g_autoptr(GPtrArray) array = NULL;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
g_return_val_if_fail (pk_is_thread_default (), FALSE);
array = pk_scheduler_get_active_transactions (scheduler);
for (i = 0; i < array->len; i++) {
item = g_ptr_array_index (array, i);
job = pk_transaction_get_backend_job (item->transaction);
if (job == NULL)
continue;
if (!pk_backend_job_get_allow_cancel (job))
return TRUE;
}
return FALSE;
}
void
pk_scheduler_cancel_background (PkScheduler *scheduler)
{
guint i;
GPtrArray *array;
PkSchedulerItem *item;
PkTransactionState state;
g_return_if_fail (PK_IS_SCHEDULER (scheduler));
g_return_if_fail (pk_is_thread_default ());
array = scheduler->priv->array;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
state = pk_transaction_get_state (item->transaction);
if (state != PK_TRANSACTION_STATE_RUNNING)
continue;
if (!pk_transaction_get_background (item->transaction))
continue;
g_debug ("cancelling running background transaction %s",
item->tid);
pk_transaction_cancel_bg (item->transaction);
}
}
void
pk_scheduler_cancel_queued (PkScheduler *scheduler)
{
guint i;
GPtrArray *array;
PkSchedulerItem *item;
PkTransactionState state;
g_return_if_fail (PK_IS_SCHEDULER (scheduler));
g_return_if_fail (pk_is_thread_default ());
array = scheduler->priv->array;
for (i = 0; i < array->len; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (array, i);
state = pk_transaction_get_state (item->transaction);
if (state >= PK_TRANSACTION_STATE_RUNNING)
continue;
g_debug ("cancelling pending transaction %s", item->tid);
pk_transaction_cancel_bg (item->transaction);
}
}
gchar **
pk_scheduler_get_array (PkScheduler *scheduler)
{
guint i;
guint length;
PkSchedulerItem *item;
PkTransactionState state;
g_autoptr(GPtrArray) parray = NULL;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), NULL);
g_return_val_if_fail (pk_is_thread_default (), NULL);
parray = g_ptr_array_new_with_free_func (g_free);
length = scheduler->priv->array->len;
for (i = 0; i < length; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (scheduler->priv->array, i);
state = pk_transaction_get_state (item->transaction);
if (state == PK_TRANSACTION_STATE_READY ||
state == PK_TRANSACTION_STATE_RUNNING)
g_ptr_array_add (parray, g_strdup (item->tid));
}
g_debug ("%i transactions in list, %i committed but not finished",
length, parray->len);
return pk_ptr_array_to_strv (parray);
}
guint
pk_scheduler_get_size (PkScheduler *scheduler)
{
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), 0);
g_return_val_if_fail (pk_is_thread_default (), 0);
return scheduler->priv->array->len;
}
gchar *
pk_scheduler_get_state (PkScheduler *scheduler)
{
guint i;
guint length;
guint running = 0;
guint waiting = 0;
guint no_commit = 0;
PkRoleEnum role;
PkSchedulerItem *item;
PkTransactionState state;
GString *string;
length = scheduler->priv->array->len;
string = g_string_new ("State:\n");
if (length == 0)
goto out;
for (i = 0; i < length; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (scheduler->priv->array, i);
state = pk_transaction_get_state (item->transaction);
if (state == PK_TRANSACTION_STATE_RUNNING)
running++;
if (state == PK_TRANSACTION_STATE_READY)
waiting++;
if (state == PK_TRANSACTION_STATE_NEW)
no_commit++;
role = pk_transaction_get_role (item->transaction);
g_string_append_printf (string, "%0i\t%s\t%s\tstate[%s] "
"exclusive[%i] background[%i]\n", i,
pk_role_enum_to_string (role), item->tid,
pk_transaction_state_to_string (state),
pk_transaction_is_exclusive (item->transaction),
pk_transaction_get_background (item->transaction));
}
if (waiting == length)
g_string_append_printf (string, "WARNING: everything is waiting!\n");
out:
return g_string_free (string, FALSE);
}
static void
pk_scheduler_print (PkScheduler *scheduler)
{
g_autofree gchar *state = NULL;
state = pk_scheduler_get_state (scheduler);
g_debug ("%s", state);
}
static gboolean
pk_scheduler_is_consistent (PkScheduler *scheduler)
{
guint i;
gboolean ret = TRUE;
guint running = 0;
guint running_exclusive = 0;
guint waiting = 0;
guint no_commit = 0;
guint length;
guint unknown_role = 0;
PkSchedulerItem *item;
PkTransactionState state;
PkRoleEnum role;
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), 0);
length = scheduler->priv->array->len;
if (length == 0)
return TRUE;
for (i = 0; i < length; i++) {
item = (PkSchedulerItem *) g_ptr_array_index (scheduler->priv->array, i);
state = pk_transaction_get_state (item->transaction);
if (state == PK_TRANSACTION_STATE_RUNNING)
running++;
if (state == PK_TRANSACTION_STATE_READY)
waiting++;
if (state == PK_TRANSACTION_STATE_NEW)
no_commit++;
role = pk_transaction_get_role (item->transaction);
if (role == PK_ROLE_ENUM_UNKNOWN)
unknown_role++;
}
if (unknown_role != 0) {
pk_scheduler_print (scheduler);
g_debug ("%i have an unknown role (CreateTransaction then nothing?)", unknown_role);
}
if (no_commit != 0) {
pk_scheduler_print (scheduler);
g_debug ("%i have not been committed and may be pending auth", no_commit);
}
if (running > 0) {
pk_scheduler_print (scheduler);
g_debug ("%i are running", running);
}
running_exclusive = pk_scheduler_get_exclusive_running (scheduler);
if (running_exclusive > 1) {
pk_scheduler_print (scheduler);
g_warning ("%i exclusive transactions running", running_exclusive);
ret = FALSE;
}
if (waiting == length) {
pk_scheduler_print (scheduler);
ret = FALSE;
}
return ret;
}
static gboolean
pk_scheduler_wedge_check (PkScheduler *scheduler)
{
g_return_val_if_fail (PK_IS_SCHEDULER (scheduler), FALSE);
if (!pk_scheduler_is_consistent (scheduler))
g_warning ("list is not consistent");
return TRUE;
}
void
pk_scheduler_set_backend (PkScheduler *scheduler,
PkBackend *backend)
{
g_return_if_fail (PK_IS_SCHEDULER (scheduler));
g_return_if_fail (PK_IS_BACKEND (backend));
g_return_if_fail (scheduler->priv->backend == NULL);
scheduler->priv->backend = g_object_ref (backend);
}
static void
pk_scheduler_class_init (PkSchedulerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pk_scheduler_finalize;
signals [PK_SCHEDULER_CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (klass, sizeof (PkSchedulerPrivate));
}
static void
pk_scheduler_init (PkScheduler *scheduler)
{
scheduler->priv = PK_SCHEDULER_GET_PRIVATE (scheduler);
scheduler->priv->array = g_ptr_array_new ();
scheduler->priv->introspection = pk_load_introspection (PK_DBUS_INTERFACE_TRANSACTION ".xml",
NULL);
scheduler->priv->unwedge_id = g_timeout_add_seconds (PK_TRANSACTION_WEDGE_CHECK,
(GSourceFunc) pk_scheduler_wedge_check, scheduler);
g_source_set_name_by_id (scheduler->priv->unwedge_id, "[PkScheduler] wedge-check (main)");
}
static void
pk_scheduler_finalize (GObject *object)
{
PkScheduler *scheduler;
g_return_if_fail (PK_IS_SCHEDULER (object));
scheduler = PK_SCHEDULER (object);
g_return_if_fail (scheduler->priv != NULL);
if (scheduler->priv->unwedge_id != 0)
g_source_remove (scheduler->priv->unwedge_id);
g_ptr_array_foreach (scheduler->priv->array,
(GFunc) pk_scheduler_item_free_cb, NULL);
g_ptr_array_free (scheduler->priv->array, TRUE);
g_dbus_node_info_unref (scheduler->priv->introspection);
g_key_file_unref (scheduler->priv->conf);
if (scheduler->priv->backend != NULL)
g_object_unref (scheduler->priv->backend);
G_OBJECT_CLASS (pk_scheduler_parent_class)->finalize (object);
}
PkScheduler *
pk_scheduler_new (GKeyFile *conf)
{
PkScheduler *scheduler = PK_SCHEDULER (g_object_new (PK_TYPE_SCHEDULER, NULL));
scheduler->priv->conf = g_key_file_ref (conf);
return scheduler;
}