From ab817d2e96294e02572d94248d24a419f16a059a Mon Sep 17 00:00:00 2001 From: Jakub Filak Date: Tue, 8 Mar 2016 15:24:08 +0100 Subject: [PATCH] Introduce Tasks --- service/report-daemon.cpp | 161 +++++++++++++++++++++++++++++++++---- service/report-daemon.h | 26 +++++- service/report-service.cpp | 104 ++++++------------------ service/report-task.cpp | 32 +++++++- service/report-task.h | 5 +- tests/api-sanity-test | 132 ++++++++++++++++++++++++++++++ 6 files changed, 355 insertions(+), 105 deletions(-) create mode 100755 tests/api-sanity-test diff --git a/service/report-daemon.cpp b/service/report-daemon.cpp index d04fcab..f0a062b 100644 --- a/service/report-daemon.cpp +++ b/service/report-daemon.cpp @@ -17,32 +17,158 @@ #include "report-task.h" #include "report-dbus-constants.h" +#include #include #include +#include + using namespace Glib; static RefPtr s_main_loop; -static GDBusObjectManagerServer *object_manager; -static void -on_name_acquired(GDBusConnection *connection, +class ReportDaemonPrivate { + public: + GDBusObjectManagerServer *object_manager; + ReportService *report_service; + + bool connected() { return this->object_manager != 0; } +}; + +ReportDaemon::ReportDaemon() : + d(new ReportDaemonPrivate()) +{} + +ReportDaemon::~ReportDaemon() +{ + delete d; +} + +/* static */ ReportDaemon & +ReportDaemon::inst() +{ + static ReportDaemon daemon; + + return daemon; +} + +std::string +ReportDaemon::get_problem_directory(const std::string &problem_entry) +{ + std::string problem_dir("/var/tmp"); + problem_dir.append(problem_entry.begin() + problem_entry.find_last_of('/'), problem_entry.end()); + + if (!access(problem_dir.c_str(), R_OK)) + return problem_dir; + + auto cancellable = Gio::Cancellable::create(); + auto connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM, + cancellable); + if (!connection) { + throw Gio::DBus::Error(Gio::DBus::Error::FAILED, + "Cannot get system bus"); + } + + auto info = Glib::RefPtr(); + auto entry = Gio::DBus::Proxy::create_sync(connection, + "org.freedesktop.problems", + problem_entry, + "org.freedesktop.Problems2.Entry", + cancellable, + info, + Gio::DBus::PROXY_FLAGS_NONE); + + if (!entry) { + throw Gio::DBus::Error(Gio::DBus::Error::INVALID_ARGS, + "Problems2 Entry is not accessible"); + } + + + Glib::Variant > elements; + entry->get_cached_property(elements, "Elements"); + + if (!elements) { + throw Gio::DBus::Error(Gio::DBus::Error::FAILED, + "Problems2 Entry does not have property Elements"); + } + + auto elem_vector = elements.get(); + const size_t elems(elem_vector.size()); + const size_t dbus_fd_limit(16); + + struct dump_dir *dd = dd_create_skeleton(problem_dir.c_str(), -1, 0600, 0); + + for (size_t batch = 0; batch < elems; batch += dbus_fd_limit) { + const size_t range(batch + dbus_fd_limit); + auto end(range > elems ? elem_vector.end() : elem_vector.begin() + range); + std::vector b(elem_vector.begin() + batch, end); + + auto parameters = Glib::VariantContainerBase::create_tuple({Glib::Variant >::create(b), + Glib::Variant::create(1)}); + + auto in_fds = Gio::UnixFDList::create(); + auto out_fds = Gio::UnixFDList::create(); + auto reply = entry->call_sync("ReadElements", + parameters, + cancellable, + in_fds, + out_fds, + -1); + + batch = range; + + Glib::Variant > data; + reply.get_child(data); + for (auto kv : data.get()) { + Glib::Variant fd_pos = Glib::VariantBase::cast_dynamic< Glib::Variant >(kv.second); + + int fd = out_fds->get(fd_pos.get()); + dd_copy_fd(dd, kv.first.c_str(), fd, 0, 0); + close(fd); + } + } + dd_close(dd); + return problem_dir; +} + +void +ReportDaemon::settle_connection(GDBusConnection *connection) +{ + if (d->connected()) { + g_warning("report-daemon already settled a connection"); + return; + } + + d->object_manager = g_dbus_object_manager_server_new(REPORTD_DBUS_OBJECT_MANAGER_PATH); + + d->report_service = report_service_new(REPORTD_DBUS_SERVICE_PATH); + g_dbus_object_manager_server_export(d->object_manager, G_DBUS_OBJECT_SKELETON(d->report_service)); + + g_dbus_object_manager_server_set_connection(d->object_manager, connection); +} + +/* static */ void +ReportDaemon::on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer ) { g_debug("Session bus with '%s' acquired", name); + ReportDaemon::inst().settle_connection(connection); +} - object_manager = g_dbus_object_manager_server_new(REPORTD_DBUS_OBJECT_MANAGER_PATH); - - ReportService *service = report_service_new(REPORTD_DBUS_SERVICE_PATH); - g_dbus_object_manager_server_export(object_manager, G_DBUS_OBJECT_SKELETON(service)); - - ReportTask *task = report_task_new(REPORTD_DBUS_TASK_BASE_PATH "1"); - g_dbus_object_manager_server_export(object_manager, G_DBUS_OBJECT_SKELETON(task)); +void +ReportDaemon::register_object(GDBusObjectSkeleton *object) +{ + if (!d->connected()) { + /* TODO : throw an exception if the daemon isn't settled yet */ + g_warning("report-daemon not yet settled a connection: cannot register an object"); + return; + } - g_dbus_object_manager_server_set_connection(object_manager, connection); + g_dbus_object_manager_server_export(d->object_manager, object); } + static void on_name_lost(GDBusConnection *, const gchar *name, @@ -52,10 +178,11 @@ on_name_lost(GDBusConnection *, s_main_loop->quit(); } -void -on_signal_quit(int) +static gboolean +on_signal_quit(gpointer data) { - s_main_loop->quit(); + (*static_cast *>(data))->quit(); + return FALSE; } int @@ -67,15 +194,15 @@ main(void) REPORTD_DBUS_BUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, - on_name_acquired, + ReportDaemon::on_name_acquired, on_name_lost, NULL, NULL); s_main_loop = MainLoop::create(); - //signal(SIGINT, on_signal_quit); - //signal(SIGTERM, on_signal_quit); + g_unix_signal_add(SIGINT, on_signal_quit, &s_main_loop); + g_unix_signal_add(SIGTERM, on_signal_quit, &s_main_loop); s_main_loop->run(); diff --git a/service/report-daemon.h b/service/report-daemon.h index bd7a11a..297fb63 100644 --- a/service/report-daemon.h +++ b/service/report-daemon.h @@ -19,8 +19,30 @@ #include -G_BEGIN_DECLS +class ReportDaemon { + public: + std::string get_problem_directory (const std::string &); + + void register_object (GDBusObjectSkeleton *); + + + static ReportDaemon& inst(); + + static void on_name_acquired (GDBusConnection *, + const gchar *, + gpointer); + + private: + void settle_connection(GDBusConnection *); + + ReportDaemon(); + ReportDaemon(const ReportDaemon &) = delete; + ReportDaemon& operator=(const ReportDaemon &) = delete; + + class ReportDaemonPrivate *d; + + ~ReportDaemon(); +}; -G_END_DECLS #endif /*__REPORT_DAEMON_H__*/ diff --git a/service/report-service.cpp b/service/report-service.cpp index 52cb5f9..a56ea8d 100644 --- a/service/report-service.cpp +++ b/service/report-service.cpp @@ -14,6 +14,9 @@ #include "config.h" #include "report-service.h" +#include "report-task.h" +#include "report-daemon.h" +#include "report-dbus-constants.h" #include #include @@ -26,94 +29,32 @@ G_DEFINE_TYPE(ReportService, report_service, G_TYPE_DBUS_OBJECT_SKELETON); struct _ReportServicePrivate { ReportDbusService *service_iface; + unsigned long task_cnt; }; static gboolean -report_service_handle_create_task(ReportDbusService * /*object*/, - GDBusMethodInvocation * /*invocation*/, - const gchar * /*arg_workflow*/, - const gchar * /*arg_problem*/) +report_service_handle_create_task(ReportDbusService * /*object*/, + GDBusMethodInvocation *invocation, + const gchar *arg_workflow, + const gchar *arg_problem, + ReportService *self) { - return TRUE; -} - -static std::string -get_problem_directory(const std::string &problem_entry) -{ - std::string problem_dir("/var/tmp"); - problem_dir.append(problem_entry.begin() + problem_entry.find_last_of('/'), problem_entry.end()); - - if (!access(problem_dir.c_str(), R_OK)) - return problem_dir; - - auto cancellable = Gio::Cancellable::create(); - auto connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM, - cancellable); - if (!connection) { - throw Gio::DBus::Error(Gio::DBus::Error::FAILED, - "Cannot get system bus"); - } - - auto info = Glib::RefPtr(); - auto entry = Gio::DBus::Proxy::create_sync(connection, - "org.freedesktop.problems", - problem_entry, - "org.freedesktop.Problems2.Entry", - cancellable, - info, - Gio::DBus::PROXY_FLAGS_NONE); - - if (!entry) { - throw Gio::DBus::Error(Gio::DBus::Error::INVALID_ARGS, - "Problems2 Entry is not accessible"); - } - - - Glib::Variant > elements; - entry->get_cached_property(elements, "Elements"); - - if (!elements) { - throw Gio::DBus::Error(Gio::DBus::Error::FAILED, - "Problems2 Entry does not have property Elements"); + if (self->pv->task_cnt == ULONG_MAX) { + g_dbus_method_invocation_return_error(invocation, + G_DBUS_ERROR, G_DBUS_ERROR_FAILED, + "Reportd Service cannot create a new task"); + return TRUE; } - auto elem_vector = elements.get(); - const size_t elems(elem_vector.size()); - const size_t dbus_fd_limit(16); - - struct dump_dir *dd = dd_create_skeleton(problem_dir.c_str(), -1, 0600, 0); - - for (size_t batch = 0; batch < elems; batch += dbus_fd_limit) { - const size_t range(batch + dbus_fd_limit); - auto end(range > elems ? elem_vector.end() : elem_vector.begin() + range); - std::vector b(elem_vector.begin() + batch, end); - - auto parameters = Glib::VariantContainerBase::create_tuple({Glib::Variant >::create(b), - Glib::Variant::create(1)}); + unsigned long task_id = self->pv->task_cnt++; + std::string task_path(std::string(REPORTD_DBUS_TASK_BASE_PATH) + std::to_string(task_id)); + ReportTask *t = report_task_new(task_path.c_str(), arg_workflow, arg_problem); + ReportDaemon::inst().register_object(G_DBUS_OBJECT_SKELETON(t)); - auto in_fds = Gio::UnixFDList::create(); - auto out_fds = Gio::UnixFDList::create(); - auto reply = entry->call_sync("ReadElements", - parameters, - cancellable, - in_fds, - out_fds, - -1); + GVariant *retval = g_variant_new("(o)", task_path.c_str()); + g_dbus_method_invocation_return_value(invocation, retval); - batch = range; - - Glib::Variant > data; - reply.get_child(data); - for (auto kv : data.get()) { - Glib::Variant fd_pos = Glib::VariantBase::cast_dynamic< Glib::Variant >(kv.second); - - int fd = out_fds->get(fd_pos.get()); - dd_copy_fd(dd, kv.first.c_str(), fd, 0, 0); - close(fd); - } - } - dd_close(dd); - return problem_dir; + return TRUE; } static gboolean @@ -124,7 +65,7 @@ report_service_handle_get_workflows(ReportDbusService * /*object*/, std::string problem_dir; try { - problem_dir = get_problem_directory(arg_problem); + problem_dir = ReportDaemon::inst().get_problem_directory(arg_problem); } catch (const Glib::Error &err) { g_dbus_method_invocation_return_error(invocation, @@ -173,6 +114,7 @@ report_service_init(ReportService *self) self->pv = G_TYPE_INSTANCE_GET_PRIVATE(self, REPORT_TYPE_SERVICE, ReportServicePrivate); self->pv->service_iface = report_dbus_service_skeleton_new(); + self->pv->task_cnt = 1; g_signal_connect(self->pv->service_iface, "handle-create-task", diff --git a/service/report-task.cpp b/service/report-task.cpp index f1ec5d8..dbb4f1b 100644 --- a/service/report-task.cpp +++ b/service/report-task.cpp @@ -21,19 +21,25 @@ G_DEFINE_TYPE(ReportTask, report_task, G_TYPE_DBUS_OBJECT_SKELETON); struct _ReportTaskPrivate { ReportDbusTask *task_iface; + gchar *problem_path; + gchar *workflow_id; }; static gboolean report_task_handle_start(ReportDbusTask * /*object*/, - GDBusMethodInvocation * /*invocation*/) + GDBusMethodInvocation *invocation) { + g_message("Started task!"); + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); return TRUE; } static gboolean report_task_handle_cancel(ReportDbusTask * /*object*/, - GDBusMethodInvocation * /*invocation*/) + GDBusMethodInvocation *invocation) { + g_message("Canceled task!"); + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); return TRUE; } @@ -65,21 +71,39 @@ report_task_constructed(GObject *obj) report_dbus_task_set_status(self->pv->task_iface, "NEW"); } +static void +report_task_dispose(GObject *obj) +{ + ReportTask *self = REPORT_TASK(obj); + + g_free(self->pv->problem_path); + g_free(self->pv->workflow_id); + + G_OBJECT_CLASS(report_task_parent_class)->finalize(obj); +} + static void report_task_class_init(ReportTaskClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = report_task_constructed; + object_class->dispose = report_task_dispose; g_type_class_add_private (klass, sizeof (ReportTaskPrivate)); } -ReportTask *report_task_new(const gchar *object_path) +ReportTask *report_task_new(const gchar *object_path, + const gchar *problem_path, + const gchar *workflow_id) { gpointer object = g_object_new(REPORT_TYPE_TASK, "g-object-path", object_path, NULL); - return static_cast(object); + ReportTask *task = static_cast(object); + task->pv->problem_path = g_strdup(problem_path); + task->pv->workflow_id = g_strdup(workflow_id); + + return task; } diff --git a/service/report-task.h b/service/report-task.h index 00fe2f2..15df2cc 100644 --- a/service/report-task.h +++ b/service/report-task.h @@ -44,7 +44,10 @@ struct _ReportTaskClass { }; GType report_task_get_type(void); -ReportTask *report_task_new (const gchar *object_path); + +ReportTask *report_task_new (const gchar *object_path, + const gchar *problem_path, + const gchar *workflow_id); G_END_DECLS diff --git a/tests/api-sanity-test b/tests/api-sanity-test new file mode 100755 index 0000000..3178ac0 --- /dev/null +++ b/tests/api-sanity-test @@ -0,0 +1,132 @@ +#!/usr/bin/python3 + +import dbus + + +class DBusObject(object): + + def __init__(self, bus, address, obj_path, interface): + obj_proxy = bus.get_object(address, obj_path) + + self._properties = dbus.Interface( + obj_proxy, + dbus_interface="org.freedesktop.DBus.Properties") + + self._interface = interface + self._obj = dbus.Interface(obj_proxy, dbus_interface=interface) + + def __getattribute__(self, name): + try: + return object.__getattribute__(self, name) + except AttributeError: + obj = object.__getattribute__(self, "_obj") + return obj.get_dbus_method(name) + + def getobject(self): + return object.__getattribute__(self, "_obj") + + def getobjectproperties(self): + return object.__getattribute__(self, "_properties") + + def getproperty(self, name): + properties = object.__getattribute__(self, "_properties") + interface = object.__getattribute__(self, "_interface") + return properties.Get(interface, name) + + +class Problems2Object(DBusObject): + + def __init__(self, bus, path, iface): + super(Problems2Object, self).__init__( + bus, + "org.freedesktop.problems", + path, + iface) + + +class Problems2Service(Problems2Object): + + def __init__(self, bus, path): + super(Problems2Service, self).__init__( + bus, + path, + "org.freedesktop.Problems2") + + +class Problems2Entry(Problems2Object): + + def __init__(self, bus, path): + super(Problems2Entry, self).__init__( + bus, + path, + "org.freedesktop.Problems2.Entry") + + +class Problems2Session(Problems2Object): + + def __init__(self, bus, path): + super(Problems2Session, self).__init__( + bus, + path, + "org.freedesktop.Problems2.Session") + + +class Problems2Task(Problems2Object): + + def __init__(self, bus, path): + super(Problems2Task, self).__init__( + bus, + path, + "org.freedesktop.Problems2.Task") + + +class ReportdObject(DBusObject): + + def __init__(self, bus, path, iface): + super(ReportdObject, self).__init__( + bus, + "org.freedesktop.reportd", + path, + iface) + + +class ReportdService(ReportdObject): + + def __init__(self, bus): + super(ReportdService, self).__init__( + bus, + "/org/freedesktop/reportd/Service", + "org.freedesktop.reportd.Service") + + +class ReportdTask(ReportdObject): + + def __init__(self, bus, path): + super(ReportdTask, self).__init__( + bus, + path, + "org.freedesktop.reportd.Task") + + +system_bus = dbus.SystemBus() +session_bus = dbus.SessionBus() + +p2 = Problems2Service(system_bus, '/org/freedesktop/Problems2') +rd = ReportdService(session_bus) + +cnt = 1 +for pobj in p2.GetProblems(0, {}): + entry = Problems2Entry(system_bus, pobj) + print("{0} : {1}".format(cnt, entry.getproperty("Executable"))) + + wfs = rd.GetWorkflows(pobj) + cnt += 1 + for wf_id, wf_name, wf_description in wfs: + print("\t{1} - ({0}) - {2}".format(wf_id, wf_name, wf_description)) + + tobj = rd.CreateTask(wfs[0][0], pobj) + print("Task - {0}".format(tobj)) + + task = ReportdTask(session_bus, tobj) + task.Start() + task.Cancel()