Blame src/dbus/abrt_problems2_task.c

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