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