Blame src/dbus/abrt_problems2_task.c

Packit Service 8a8a03
/*
Packit Service 8a8a03
  Copyright (C) 2015  ABRT team
Packit Service 8a8a03
Packit Service 8a8a03
  This program is free software; you can redistribute it and/or modify
Packit Service 8a8a03
  it under the terms of the GNU General Public License as published by
Packit Service 8a8a03
  the Free Software Foundation; either version 2 of the License, or
Packit Service 8a8a03
  (at your option) any later version.
Packit Service 8a8a03
Packit Service 8a8a03
  This program is distributed in the hope that it will be useful,
Packit Service 8a8a03
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 8a8a03
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 8a8a03
  GNU General Public License for more details.
Packit Service 8a8a03
Packit Service 8a8a03
  You should have received a copy of the GNU General Public License along
Packit Service 8a8a03
  with this program; if not, write to the Free Software Foundation, Inc.,
Packit Service 8a8a03
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Packit Service 8a8a03
*/
Packit Service 8a8a03
Packit Service 8a8a03
#include "abrt_problems2_task.h"
Packit Service 8a8a03
Packit Service 8a8a03
enum {
Packit Service 8a8a03
    SN_STATUS_CHANGED,
Packit Service 8a8a03
    SN_LAST_SIGNAL
Packit Service 8a8a03
} SignalNumber;
Packit Service 8a8a03
Packit Service 8a8a03
static guint s_signals[SN_LAST_SIGNAL] = { 0 };
Packit Service 8a8a03
Packit Service 8a8a03
G_DEFINE_TYPE_WITH_PRIVATE(AbrtP2Task, abrt_p2_task, G_TYPE_OBJECT)
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_finalize(GObject *gobject)
Packit Service 8a8a03
{
Packit Service 8a8a03
    AbrtP2TaskPrivate *pv = abrt_p2_task_get_instance_private(ABRT_P2_TASK(gobject));
Packit Service 8a8a03
    g_variant_unref(pv->p2t_details);
Packit Service 8a8a03
Packit Service 8a8a03
    if (pv->p2t_results != NULL)
Packit Service 8a8a03
        g_variant_unref(pv->p2t_results);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_class_init(AbrtP2TaskClass *klass)
Packit Service 8a8a03
{
Packit Service 8a8a03
    GObjectClass *object_class = G_OBJECT_CLASS(klass);
Packit Service 8a8a03
    object_class->finalize = abrt_p2_task_finalize;
Packit Service 8a8a03
Packit Service 8a8a03
    s_signals[SN_STATUS_CHANGED] = g_signal_new ("status-changed",
Packit Service 8a8a03
                                                 G_TYPE_FROM_CLASS (klass),
Packit Service 8a8a03
                                                 G_SIGNAL_RUN_LAST,
Packit Service 8a8a03
                                                 G_STRUCT_OFFSET(AbrtP2TaskClass, status_changed),
Packit Service 8a8a03
                                                 /*accumulator*/NULL, /*accu_data*/NULL,
Packit Service 8a8a03
                                                 g_cclosure_marshal_VOID__INT,
Packit Service 8a8a03
                                                 G_TYPE_NONE,
Packit Service 8a8a03
                                                 /*n_params*/1,
Packit Service 8a8a03
                                                 G_TYPE_INT);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
#define ABRT_P2_TASK_ABSTRACT_FUNCTION_CALL(ret, method, task, ...) \
Packit Service 8a8a03
    do { if (ABRT_P2_TASK_GET_CLASS(task)->method == NULL) { \
Packit Service 8a8a03
            error_msg("Undefined Task abstract function "#method); \
Packit Service 8a8a03
            abort(); \
Packit Service 8a8a03
         }\
Packit Service 8a8a03
         ret = ABRT_P2_TASK_GET_CLASS(task)->method(task, __VA_ARGS__); } while(0)
Packit Service 8a8a03
Packit Service 8a8a03
#define ABRT_P2_TASK_VIRTUAL_FUNCTION_CALL(method, task, ...) \
Packit Service 8a8a03
    do { if (ABRT_P2_TASK_GET_CLASS(task)->method != NULL) \
Packit Service 8a8a03
            ABRT_P2_TASK_GET_CLASS(task)->method(task, __VA_ARGS__); } while(0)
Packit Service 8a8a03
Packit Service 8a8a03
#define ABRT_P2_TASK_VIRTUAL_CANCEL(task, error) \
Packit Service 8a8a03
    ABRT_P2_TASK_VIRTUAL_FUNCTION_CALL(cancel, task, error)
Packit Service 8a8a03
Packit Service 8a8a03
#define ABRT_P2_TASK_VIRTUAL_FINISH(task, error) \
Packit Service 8a8a03
    ABRT_P2_TASK_VIRTUAL_FUNCTION_CALL(finish, task, error)
Packit Service 8a8a03
Packit Service 8a8a03
#define ABRT_P2_TASK_VIRTUAL_START(task, options, error) \
Packit Service 8a8a03
    ABRT_P2_TASK_VIRTUAL_FUNCTION_CALL(start, task, options, error)
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_init(AbrtP2Task *self)
Packit Service 8a8a03
{
Packit Service 8a8a03
    self->pv = abrt_p2_task_get_instance_private(self);
Packit Service 8a8a03
    self->pv->p2t_details = g_variant_new("a{sv}", NULL);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_change_status(AbrtP2Task *task,
Packit Service 8a8a03
            AbrtP2TaskStatus status)
Packit Service 8a8a03
{
Packit Service 8a8a03
    if (task->pv->p2t_status == status)
Packit Service 8a8a03
        return;
Packit Service 8a8a03
Packit Service 8a8a03
    task->pv->p2t_status = status;
Packit Service 8a8a03
Packit Service 8a8a03
    g_signal_emit(task,
Packit Service 8a8a03
                  s_signals[SN_STATUS_CHANGED],
Packit Service 8a8a03
                  0,
Packit Service 8a8a03
                  status);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
AbrtP2TaskStatus abrt_p2_task_status(AbrtP2Task *task)
Packit Service 8a8a03
{
Packit Service 8a8a03
    return task->pv->p2t_status;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
GVariant *abrt_p2_task_details(AbrtP2Task *task)
Packit Service 8a8a03
{
Packit Service 8a8a03
    return g_variant_ref(task->pv->p2t_details);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void abrt_p2_task_add_detail(AbrtP2Task *task,
Packit Service 8a8a03
            const char *key,
Packit Service 8a8a03
            GVariant *value)
Packit Service 8a8a03
{
Packit Service 8a8a03
    GVariantDict dict;
Packit Service 8a8a03
    g_variant_dict_init(&dict, task->pv->p2t_details);
Packit Service 8a8a03
    g_variant_dict_insert(&dict, key, "v", value);
Packit Service 8a8a03
Packit Service 8a8a03
    if (task->pv->p2t_details)
Packit Service 8a8a03
        g_variant_unref(task->pv->p2t_details);
Packit Service 8a8a03
Packit Service 8a8a03
    task->pv->p2t_details = g_variant_dict_end(&dict);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void abrt_p2_task_set_response(AbrtP2Task *task,
Packit Service 8a8a03
            GVariant *response)
Packit Service 8a8a03
{
Packit Service 8a8a03
    if (task->pv->p2t_results != NULL)
Packit Service 8a8a03
        log_warning("Task already has response assigned");
Packit Service 8a8a03
Packit Service 8a8a03
    task->pv->p2t_results = response;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
bool abrt_p2_task_is_cancelled(AbrtP2Task *task)
Packit Service 8a8a03
{
Packit Service 8a8a03
    return (task->pv->p2t_cancellable
Packit Service 8a8a03
              && g_cancellable_is_cancelled(task->pv->p2t_cancellable))
Packit Service 8a8a03
           || task->pv->p2t_status == ABRT_P2_TASK_STATUS_CANCELED;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void abrt_p2_task_cancel(AbrtP2Task *task,
Packit Service 8a8a03
            GError **error)
Packit Service 8a8a03
{
Packit Service 8a8a03
    if (abrt_p2_task_is_cancelled(task))
Packit Service 8a8a03
        return;
Packit Service 8a8a03
Packit Service 8a8a03
    if (task->pv->p2t_status == ABRT_P2_TASK_STATUS_RUNNING)
Packit Service 8a8a03
        g_cancellable_cancel(task->pv->p2t_cancellable);
Packit Service 8a8a03
    else if (task->pv->p2t_status == ABRT_P2_TASK_STATUS_STOPPED)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        ABRT_P2_TASK_VIRTUAL_CANCEL(task, error);
Packit Service 8a8a03
Packit Service 8a8a03
        if (*error == NULL)
Packit Service 8a8a03
            abrt_p2_task_change_status(task, ABRT_P2_TASK_STATUS_CANCELED);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else
Packit Service 8a8a03
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
Packit Service 8a8a03
                    "Task is not in the state that allows cancelling");
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void abrt_p2_task_finish(AbrtP2Task *task,
Packit Service 8a8a03
            GVariant **result,
Packit Service 8a8a03
            gint32 *code,
Packit Service 8a8a03
            GError **error)
Packit Service 8a8a03
{
Packit Service 8a8a03
    if (   task->pv->p2t_status != ABRT_P2_TASK_STATUS_DONE
Packit Service 8a8a03
        && task->pv->p2t_status != ABRT_P2_TASK_STATUS_FAILED)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
Packit Service 8a8a03
                "Cannot finalize undone task");
Packit Service 8a8a03
        return;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    ABRT_P2_TASK_VIRTUAL_FINISH(task, error);
Packit Service 8a8a03
Packit Service 8a8a03
    if (*error != NULL)
Packit Service 8a8a03
        return;
Packit Service 8a8a03
Packit Service 8a8a03
    if (task->pv->p2t_results)
Packit Service 8a8a03
        *result = g_variant_ref(task->pv->p2t_results);
Packit Service 8a8a03
    else
Packit Service 8a8a03
        *result = g_variant_new("a{sv}", NULL);
Packit Service 8a8a03
Packit Service 8a8a03
    *code = task->pv->p2t_code;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_finish_gtask(GObject *source_object,
Packit Service 8a8a03
           GAsyncResult *result,
Packit Service 8a8a03
           gpointer user_data)
Packit Service 8a8a03
{
Packit Service 8a8a03
    AbrtP2Task *task = ABRT_P2_TASK(source_object);
Packit Service 8a8a03
Packit Service 8a8a03
    if (!g_task_is_valid(result, task))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        error_msg("BUG:%s:%s: invalid GTask", __FILE__, __func__);
Packit Service 8a8a03
        return;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    GError *error = NULL;
Packit Service 8a8a03
    const gint32 code = g_task_propagate_int(G_TASK(result), &error);
Packit Service 8a8a03
Packit Service 8a8a03
    if (code == ABRT_P2_TASK_CODE_STOP)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_debug("Task stopped");
Packit Service 8a8a03
Packit Service 8a8a03
        abrt_p2_task_change_status(task, ABRT_P2_TASK_STATUS_STOPPED);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else if (code >= ABRT_P2_TASK_CODE_DONE)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_debug("Task done");
Packit Service 8a8a03
Packit Service 8a8a03
        task->pv->p2t_code = code - ABRT_P2_TASK_CODE_DONE;
Packit Service 8a8a03
        abrt_p2_task_change_status(task, ABRT_P2_TASK_STATUS_DONE);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else if (abrt_p2_task_is_cancelled(task))
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (error != NULL)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            log_debug("Task canceled with error: %s", error->message);
Packit Service 8a8a03
Packit Service 8a8a03
            g_error_free(error);
Packit Service 8a8a03
            error = NULL;
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else
Packit Service 8a8a03
            log_debug("Task canceled");
Packit Service 8a8a03
Packit Service 8a8a03
        ABRT_P2_TASK_VIRTUAL_CANCEL(task, &error);
Packit Service 8a8a03
        abrt_p2_task_change_status(task, ABRT_P2_TASK_STATUS_CANCELED);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else
Packit Service 8a8a03
    {
Packit Service 8a8a03
        GVariantDict response;
Packit Service 8a8a03
        g_variant_dict_init(&response, NULL);
Packit Service 8a8a03
Packit Service 8a8a03
        if (error != NULL)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            log_debug("Task failed with error: %s", error->message);
Packit Service 8a8a03
Packit Service 8a8a03
            g_variant_dict_insert(&response,
Packit Service 8a8a03
                                  "Error.Message",
Packit Service 8a8a03
                                  "s",
Packit Service 8a8a03
                                  error->message);
Packit Service 8a8a03
Packit Service 8a8a03
            g_error_free(error);
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else if (code == ABRT_P2_TASK_CODE_ERROR)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            log_debug("Task failed without error message");
Packit Service 8a8a03
Packit Service 8a8a03
            g_variant_dict_insert(&response,
Packit Service 8a8a03
                                  "Error.Message",
Packit Service 8a8a03
                                  "s",
Packit Service 8a8a03
                                  "Task failed");
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else
Packit Service 8a8a03
        {
Packit Service 8a8a03
            error_msg("BUG:%s:%s: unknown Task return code: %d",
Packit Service 8a8a03
                      __FILE__,
Packit Service 8a8a03
                      __func__,
Packit Service 8a8a03
                      code);
Packit Service 8a8a03
Packit Service 8a8a03
            g_variant_dict_insert(&response,
Packit Service 8a8a03
                                  "Error.Message",
Packit Service 8a8a03
                                  "s",
Packit Service 8a8a03
                                  "Internal error: Invalid Task return code");
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        abrt_p2_task_set_response(task, g_variant_dict_end(&response));
Packit Service 8a8a03
        abrt_p2_task_change_status(task, ABRT_P2_TASK_STATUS_FAILED);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    g_object_unref(task->pv->p2t_cancellable);
Packit Service 8a8a03
    task->pv->p2t_cancellable = NULL;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_thread(GTask *task,
Packit Service 8a8a03
            gpointer source_object,
Packit Service 8a8a03
            gpointer task_data,
Packit Service 8a8a03
            GCancellable *cancellable)
Packit Service 8a8a03
{
Packit Service 8a8a03
    AbrtP2Task *stask = source_object;
Packit Service 8a8a03
Packit Service 8a8a03
    GError *error = NULL;
Packit Service 8a8a03
    gint32 code;
Packit Service 8a8a03
Packit Service 8a8a03
    ABRT_P2_TASK_ABSTRACT_FUNCTION_CALL(code, run, stask, &error);
Packit Service 8a8a03
Packit Service 8a8a03
    if (error == NULL)
Packit Service 8a8a03
        g_task_return_int(task, code);
Packit Service 8a8a03
    else
Packit Service 8a8a03
        g_task_return_error(task, error);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void abrt_p2_task_start(AbrtP2Task *task,
Packit Service 8a8a03
            GVariant *options,
Packit Service 8a8a03
            GError **error)
Packit Service 8a8a03
{
Packit Service 8a8a03
    if (   task->pv->p2t_status != ABRT_P2_TASK_STATUS_NEW
Packit Service 8a8a03
        && task->pv->p2t_status != ABRT_P2_TASK_STATUS_STOPPED)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
Packit Service 8a8a03
                    "Cannot start task that is not new or stopped");
Packit Service 8a8a03
        return;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    ABRT_P2_TASK_VIRTUAL_START(task, options, error);
Packit Service 8a8a03
Packit Service 8a8a03
    if (*error != NULL)
Packit Service 8a8a03
        return;
Packit Service 8a8a03
Packit Service 8a8a03
    task->pv->p2t_cancellable = g_cancellable_new();
Packit Service 8a8a03
    GTask *gtask = g_task_new(task,
Packit Service 8a8a03
                              task->pv->p2t_cancellable,
Packit Service 8a8a03
                              abrt_p2_task_finish_gtask,
Packit Service 8a8a03
                              NULL);
Packit Service 8a8a03
Packit Service 8a8a03
    g_task_run_in_thread(gtask, abrt_p2_task_thread);
Packit Service 8a8a03
Packit Service 8a8a03
    abrt_p2_task_change_status(task, ABRT_P2_TASK_STATUS_RUNNING);
Packit Service 8a8a03
    g_object_unref(gtask);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static void abrt_p2_task_autonomous_cb(AbrtP2Task *task,
Packit Service 8a8a03
            gint32 status,
Packit Service 8a8a03
            gpointer user_data)
Packit Service 8a8a03
{
Packit Service 8a8a03
    switch(status)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        case ABRT_P2_TASK_STATUS_NEW:
Packit Service 8a8a03
            error_msg("Autonomous task has changed status to NEW");
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        case ABRT_P2_TASK_STATUS_RUNNING:
Packit Service 8a8a03
            log_debug("Autonomous task has successfully started");
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        case ABRT_P2_TASK_STATUS_STOPPED:
Packit Service 8a8a03
            {
Packit Service 8a8a03
                error_msg("Autonomous task has been stopped and will be canceled");
Packit Service 8a8a03
Packit Service 8a8a03
                GError *error = NULL;
Packit Service 8a8a03
                abrt_p2_task_cancel(task, &error);
Packit Service 8a8a03
                if (error != NULL)
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    error_msg("Failed to cancel stopped autonomous task: %s",
Packit Service 8a8a03
                               error->message);
Packit Service 8a8a03
Packit Service 8a8a03
                    g_error_free(error);
Packit Service 8a8a03
                    g_object_unref(task);
Packit Service 8a8a03
                }
Packit Service 8a8a03
            }
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        case ABRT_P2_TASK_STATUS_CANCELED:
Packit Service 8a8a03
            log_notice("Autonomous task has been canceled");
Packit Service 8a8a03
            g_object_unref(task);
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        case ABRT_P2_TASK_STATUS_FAILED:
Packit Service 8a8a03
            {
Packit Service 8a8a03
                GVariant *response;
Packit Service 8a8a03
                gint32 code;
Packit Service 8a8a03
                GError *error = NULL;
Packit Service 8a8a03
                abrt_p2_task_finish(task, &response, &code, &error);
Packit Service 8a8a03
                if (error != NULL)
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    error_msg("Failed to finish canceled task: %s", error->message);
Packit Service 8a8a03
                }
Packit Service 8a8a03
                else
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    const char *error_message;
Packit Service 8a8a03
                    g_variant_lookup(response, "Error.Message", "&s", &error_message);
Packit Service 8a8a03
                    error_msg("Autonomous task has failed: %d: %s", code, error_message);
Packit Service 8a8a03
                    g_variant_unref(response);
Packit Service 8a8a03
                }
Packit Service 8a8a03
Packit Service 8a8a03
                g_object_unref(task);
Packit Service 8a8a03
            }
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        case ABRT_P2_TASK_STATUS_DONE:
Packit Service 8a8a03
            log_notice("Autonomous task has been successfully finished");
Packit Service 8a8a03
            g_object_unref(task);
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        default:
Packit Service 8a8a03
            error_msg("BUG: %s: forgotten task state", __func__);
Packit Service 8a8a03
            break;
Packit Service 8a8a03
    }
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void abrt_p2_task_autonomous_run(AbrtP2Task *task,
Packit Service 8a8a03
        GError **error)
Packit Service 8a8a03
{
Packit Service 8a8a03
    g_signal_connect(task,
Packit Service 8a8a03
                     "status-changed",
Packit Service 8a8a03
                     G_CALLBACK(abrt_p2_task_autonomous_cb),
Packit Service 8a8a03
                     NULL);
Packit Service 8a8a03
Packit Service 8a8a03
    abrt_p2_task_start(task, NULL, error);
Packit Service 8a8a03
}