Blame src/dbus/abrt_problems2_task_new_problem.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_new_problem.h"
Packit 8ea169
#include "abrt_problems2_entry.h"
Packit 8ea169
Packit 8ea169
typedef struct
Packit 8ea169
{
Packit 8ea169
    AbrtP2Service *p2tnp_service;
Packit 8ea169
    GVariant *p2tnp_problem_info;
Packit 8ea169
    uid_t p2tnp_caller_uid;
Packit 8ea169
    GUnixFDList *p2tnp_fd_list;
Packit 8ea169
    AbrtP2Object *p2tnp_obj;        ///<< AbrtProblems2Entry
Packit 8ea169
    bool p2tnp_wait_before_notify;
Packit 8ea169
} AbrtP2TaskNewProblemPrivate;
Packit 8ea169
Packit 8ea169
struct _AbrtP2TaskNewProblem
Packit 8ea169
{
Packit 8ea169
    AbrtP2Task parent_instance;
Packit 8ea169
    AbrtP2TaskNewProblemPrivate *pv;
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static AbrtP2TaskCode abrt_p2_task_new_problem_run(AbrtP2Task *task,
Packit 8ea169
            GError **error);
Packit 8ea169
Packit 8ea169
G_DEFINE_TYPE_WITH_PRIVATE(AbrtP2TaskNewProblem, abrt_p2_task_new_problem, ABRT_TYPE_P2_TASK)
Packit 8ea169
Packit 8ea169
static void abrt_p2_task_new_problem_finalize(GObject *gobject)
Packit 8ea169
{
Packit 8ea169
    AbrtP2TaskNewProblemPrivate *pv = abrt_p2_task_new_problem_get_instance_private(ABRT_P2_TASK_NEW_PROBLEM(gobject));
Packit 8ea169
    g_variant_unref(pv->p2tnp_problem_info);
Packit 8ea169
Packit 8ea169
    if (pv->p2tnp_fd_list)
Packit 8ea169
        g_object_unref(pv->p2tnp_fd_list);
Packit 8ea169
Packit 8ea169
    G_OBJECT_CLASS(abrt_p2_task_new_problem_parent_class)->finalize(gobject);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void abrt_p2_task_remove_temporary_entry(AbrtP2TaskNewProblem *task,
Packit 8ea169
            GError **error)
Packit 8ea169
{
Packit 8ea169
    if (task->pv->p2tnp_obj == NULL)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    AbrtP2Entry *entry = abrt_p2_object_get_node(task->pv->p2tnp_obj);
Packit 8ea169
Packit 8ea169
    log_debug("Task '%p': Removing temporary entry: %s",
Packit 8ea169
              task,
Packit 8ea169
              abrt_p2_entry_problem_id(entry));
Packit 8ea169
Packit 8ea169
    abrt_p2_entry_delete(entry,
Packit 8ea169
                         /* act as super user to allow us to delete the temporary dir */0,
Packit 8ea169
                         error);
Packit 8ea169
Packit 8ea169
    abrt_p2_object_destroy(task->pv->p2tnp_obj);
Packit 8ea169
Packit 8ea169
    task->pv->p2tnp_obj = NULL;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void abrt_p2_task_new_problem_cancel(AbrtP2Task *task,
Packit 8ea169
            GError **error)
Packit 8ea169
{
Packit 8ea169
    abrt_p2_task_remove_temporary_entry(ABRT_P2_TASK_NEW_PROBLEM(task), error);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void abrt_p2_task_new_problem_class_init(AbrtP2TaskNewProblemClass *klass)
Packit 8ea169
{
Packit 8ea169
    GObjectClass *object_class = G_OBJECT_CLASS(klass);
Packit 8ea169
    object_class->finalize = abrt_p2_task_new_problem_finalize;
Packit 8ea169
Packit 8ea169
    AbrtP2TaskClass *task_class = ABRT_P2_TASK_CLASS(klass);
Packit 8ea169
    task_class->run = abrt_p2_task_new_problem_run;
Packit 8ea169
    task_class->cancel = abrt_p2_task_new_problem_cancel;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void abrt_p2_task_new_problem_init(AbrtP2TaskNewProblem *self)
Packit 8ea169
{
Packit 8ea169
    self->pv = abrt_p2_task_new_problem_get_instance_private(self);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
AbrtP2TaskNewProblem *abrt_p2_task_new_problem_new(AbrtP2Service *service,
Packit 8ea169
            GVariant *problem_info,uid_t caller_uid,
Packit 8ea169
            GUnixFDList *fd_list)
Packit 8ea169
{
Packit 8ea169
    AbrtP2TaskNewProblem *task = g_object_new(TYPE_ABRT_P2_TASK_NEW_PROBLEM, NULL);
Packit 8ea169
Packit 8ea169
    task->pv->p2tnp_service = service;
Packit 8ea169
    task->pv->p2tnp_problem_info = problem_info;
Packit 8ea169
    task->pv->p2tnp_caller_uid = caller_uid;
Packit 8ea169
    task->pv->p2tnp_fd_list = fd_list;
Packit 8ea169
    task->pv->p2tnp_wait_before_notify = false;
Packit 8ea169
Packit 8ea169
    return task;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
void abrt_p2_task_new_problem_wait_before_notify(AbrtP2TaskNewProblem *task,
Packit 8ea169
            bool value)
Packit 8ea169
{
Packit 8ea169
    task->pv->p2tnp_wait_before_notify = value;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static AbrtP2Object *abrt_p2_task_new_problem_create_directory_task(AbrtP2TaskNewProblem *task,
Packit 8ea169
            GError **error)
Packit 8ea169
{
Packit 8ea169
    int r = abrt_p2_service_user_can_create_new_problem(task->pv->p2tnp_service, task->pv->p2tnp_caller_uid);
Packit 8ea169
    if (r == 0)
Packit 8ea169
    {
Packit 8ea169
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
Packit 8ea169
                    "Too many problems have been recently created");
Packit 8ea169
Packit 8ea169
        return NULL;
Packit 8ea169
    }
Packit 8ea169
    if (r == -E2BIG)
Packit 8ea169
    {
Packit 8ea169
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
Packit 8ea169
                    "No more problems can be created");
Packit 8ea169
Packit 8ea169
        return NULL;
Packit 8ea169
    }
Packit 8ea169
    if (r < 0)
Packit 8ea169
    {
Packit 8ea169
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
Packit 8ea169
                    "Failed to check NewProblem limits");
Packit 8ea169
Packit 8ea169
        return NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char *problem_id = abrt_p2_service_save_problem(task->pv->p2tnp_service,
Packit 8ea169
                                                    task->pv->p2tnp_problem_info,
Packit 8ea169
                                                    task->pv->p2tnp_fd_list,
Packit 8ea169
                                                    task->pv->p2tnp_caller_uid, error);
Packit 8ea169
    if (*error != NULL)
Packit 8ea169
        return NULL;
Packit 8ea169
Packit 8ea169
    AbrtP2Entry *entry = abrt_p2_entry_new_with_state(problem_id,
Packit 8ea169
                                                      ABRT_P2_ENTRY_STATE_NEW);
Packit 8ea169
Packit 8ea169
    AbrtP2Object *obj = abrt_p2_service_register_entry(task->pv->p2tnp_service,
Packit 8ea169
                                                       entry,
Packit 8ea169
                                                       error);
Packit 8ea169
Packit 8ea169
    if (*error != NULL)
Packit 8ea169
        return NULL;
Packit 8ea169
Packit 8ea169
    return obj;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int abrt_p2_task_new_problem_notify_directory_task(AbrtP2TaskNewProblem *task,
Packit 8ea169
            char **new_path, GError **error)
Packit 8ea169
{
Packit 8ea169
    AbrtP2Entry *entry = abrt_p2_object_get_node(task->pv->p2tnp_obj);
Packit 8ea169
Packit 8ea169
    char *message = NULL;
Packit 8ea169
    const char *problem_id = abrt_p2_entry_problem_id(entry);
Packit 8ea169
    int r = notify_new_path_with_response(problem_id, &message);
Packit 8ea169
    if (r < 0)
Packit 8ea169
    {
Packit 8ea169
        log_debug("Task '%p': Failed to communicate with the problems daemon",
Packit 8ea169
                  task);
Packit 8ea169
Packit 8ea169
        GError *local_error = NULL;
Packit 8ea169
        abrt_p2_entry_delete(entry,
Packit 8ea169
                             /* allow us to delete the temporary dir */0,
Packit 8ea169
                             &local_error);
Packit 8ea169
Packit 8ea169
        if (local_error != NULL)
Packit 8ea169
        {
Packit 8ea169
            error_msg("Can't remove temporary problem directory: %s",
Packit 8ea169
                      local_error->message);
Packit 8ea169
Packit 8ea169
            g_error_free(local_error);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        abrt_p2_object_destroy(task->pv->p2tnp_obj);
Packit 8ea169
        task->pv->p2tnp_obj = NULL;
Packit 8ea169
Packit 8ea169
        g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
Packit 8ea169
                    "Failed to notify the new problem directory");
Packit 8ea169
Packit 8ea169
        return r;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    gint32 code;
Packit 8ea169
    log_debug("Task '%p': New path processed: %d", task, r);
Packit 8ea169
    if (r == 303)
Packit 8ea169
    {
Packit 8ea169
        /* 303 - the daemon found a local duplicate problem */
Packit 8ea169
Packit 8ea169
        abrt_p2_object_destroy(task->pv->p2tnp_obj);
Packit 8ea169
        task->pv->p2tnp_obj = NULL;
Packit 8ea169
Packit 8ea169
        AbrtP2Object *obj = abrt_p2_service_get_entry_for_problem(task->pv->p2tnp_service,
Packit 8ea169
                                                                  message,
Packit 8ea169
                                                                  ABRT_P2_SERVICE_ENTRY_LOOKUP_NOFLAGS,
Packit 8ea169
                                                                  error);
Packit 8ea169
        if (obj == NULL)
Packit 8ea169
        {
Packit 8ea169
            error_msg("Problem Entry for directory '%s' does not exist", message);
Packit 8ea169
            free(message);
Packit 8ea169
            return ABRT_P2_TASK_CODE_ERROR;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        *new_path = xstrdup(abrt_p2_object_path(obj));
Packit 8ea169
        code = ABRT_P2_TASK_NEW_PROBLEM_DUPLICATE;
Packit 8ea169
Packit 8ea169
        log_debug("Task '%p': New occurrence of '%s'",
Packit 8ea169
                  task,
Packit 8ea169
                  *new_path);
Packit 8ea169
Packit 8ea169
        /* TODO: what about to teach the service to understand task's signals? */
Packit 8ea169
        abrt_p2_service_notify_entry_object(task->pv->p2tnp_service,
Packit 8ea169
                                            obj,
Packit 8ea169
                                            error);
Packit 8ea169
    }
Packit 8ea169
    else if (r == 410)
Packit 8ea169
    {
Packit 8ea169
        /* 410 - the problem was refused by the daemon */
Packit 8ea169
        log_debug("Task '%p': Problem dropped", task);
Packit 8ea169
Packit 8ea169
        abrt_p2_object_destroy(task->pv->p2tnp_obj);
Packit 8ea169
        task->pv->p2tnp_obj = NULL;
Packit 8ea169
Packit 8ea169
        code = ABRT_P2_TASK_NEW_PROBLEM_DROPPED;
Packit 8ea169
    }
Packit 8ea169
    else if (r == 200)
Packit 8ea169
    {
Packit 8ea169
        /* 200 - the problem was accepted */
Packit 8ea169
        *new_path = xstrdup(abrt_p2_object_path(task->pv->p2tnp_obj));
Packit 8ea169
Packit 8ea169
        code = ABRT_P2_TASK_NEW_PROBLEM_ACCEPTED;
Packit 8ea169
Packit 8ea169
        abrt_p2_entry_set_state(entry, ABRT_P2_ENTRY_STATE_COMPLETE);
Packit 8ea169
Packit 8ea169
        log_debug("Task '%p': New problem '%s'", task, *new_path);
Packit 8ea169
Packit 8ea169
        /* TODO: what about to teach the service to understand task's signals? */
Packit 8ea169
        abrt_p2_service_notify_entry_object(task->pv->p2tnp_service,
Packit 8ea169
                                            task->pv->p2tnp_obj,
Packit 8ea169
                                            error);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        log_debug("Task '%p': Problem was invalid", task);
Packit 8ea169
Packit 8ea169
        abrt_p2_entry_delete(entry,
Packit 8ea169
                             /* allow us to delete the temporary dir */0,
Packit 8ea169
                             error);
Packit 8ea169
Packit 8ea169
        abrt_p2_object_destroy(task->pv->p2tnp_obj);
Packit 8ea169
        task->pv->p2tnp_obj = NULL;
Packit 8ea169
Packit 8ea169
        code = ABRT_P2_TASK_NEW_PROBLEM_INVALID_DATA;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    free(message);
Packit 8ea169
    return code;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static AbrtP2TaskCode abrt_p2_task_new_problem_run(AbrtP2Task *task, GError **error)
Packit 8ea169
{
Packit 8ea169
    AbrtP2TaskNewProblem *np = ABRT_P2_TASK_NEW_PROBLEM(task);
Packit 8ea169
Packit 8ea169
    /* Create the temporary entry. If you ask the question how it is possible
Packit 8ea169
     * that the object already exist, then the answer is that if user requested
Packit 8ea169
     * to stop the task after creation of the object. */
Packit 8ea169
    if (np->pv->p2tnp_obj == NULL)
Packit 8ea169
    {
Packit 8ea169
        AbrtP2Object *obj = abrt_p2_task_new_problem_create_directory_task(np, error);
Packit 8ea169
        if (obj == NULL)
Packit 8ea169
            return ABRT_P2_TASK_CODE_ERROR;
Packit 8ea169
Packit 8ea169
        const char *temporary_entry_path = abrt_p2_object_path(obj);
Packit 8ea169
        GVariant *detail_path = g_variant_new_string(temporary_entry_path);
Packit 8ea169
        abrt_p2_task_add_detail(task, "NewProblem.TemporaryEntry", detail_path);
Packit 8ea169
Packit 8ea169
        log_debug("Created temporary entry '%s' for task '%p'",
Packit 8ea169
                  temporary_entry_path,
Packit 8ea169
                  task);
Packit 8ea169
Packit 8ea169
        np->pv->p2tnp_obj = obj;
Packit 8ea169
Packit 8ea169
        if (abrt_p2_task_is_cancelled(task))
Packit 8ea169
            return ABRT_P2_TASK_CODE_CANCELLED;
Packit 8ea169
Packit 8ea169
        if (np->pv->p2tnp_wait_before_notify)
Packit 8ea169
        {
Packit 8ea169
            /* Stop the task to allow users to modify the temporary object */
Packit 8ea169
            log_debug("Stopping NewProblem task '%p'", task);
Packit 8ea169
Packit 8ea169
            return ABRT_P2_TASK_CODE_STOP;
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char *new_path = NULL;
Packit 8ea169
    gint32 code = abrt_p2_task_new_problem_notify_directory_task(np,
Packit 8ea169
                                                                 &new_path,
Packit 8ea169
                                                                 error);
Packit 8ea169
    if (code < 0)
Packit 8ea169
        return ABRT_P2_TASK_CODE_ERROR;
Packit 8ea169
Packit 8ea169
    GVariantDict response;
Packit 8ea169
    g_variant_dict_init(&response, NULL);
Packit 8ea169
Packit 8ea169
    if (new_path != NULL)
Packit 8ea169
    {
Packit 8ea169
        g_variant_dict_insert(&response,
Packit 8ea169
                              "NewProblem.Entry",
Packit 8ea169
                              "s",
Packit 8ea169
                              new_path);
Packit 8ea169
        free(new_path);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    log_debug("NewProblem task '%p' has successfully finished", task);
Packit 8ea169
Packit 8ea169
    abrt_p2_task_set_response(task,
Packit 8ea169
                              g_variant_dict_end(&response));
Packit 8ea169
Packit 8ea169
    return ABRT_P2_TASK_CODE_DONE + code;
Packit 8ea169
}