diff --git a/src/meson.build b/src/meson.build index a6c07178..16710d66 100644 --- a/src/meson.build +++ b/src/meson.build @@ -19,6 +19,12 @@ ipc_service_src = gnome.gdbus_codegen('ipc-service', namespace: 'Ipc', ) +ipc_legacy_src = gnome.gdbus_codegen('ipc-legacy', + sources: 'org.gnome.Sysprof2.xml', + interface_prefix: 'org.gnome.', + namespace: 'IpcLegacy', +) + install_data(['org.gnome.Sysprof3.Profiler.xml', 'org.gnome.Sysprof3.Service.xml'], install_dir: join_paths(datadir, 'dbus-1/interfaces'), diff --git a/src/org.gnome.Sysprof2.xml b/src/org.gnome.Sysprof2.xml new file mode 100644 index 00000000..b22de4fa --- /dev/null +++ b/src/org.gnome.Sysprof2.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/sysprofd/ipc-legacy-impl.c b/src/sysprofd/ipc-legacy-impl.c new file mode 100644 index 00000000..78157b63 --- /dev/null +++ b/src/sysprofd/ipc-legacy-impl.c @@ -0,0 +1,216 @@ +/* ipc-legacy-impl.c + * + * Copyright 2019 Christian Hergert + * + * 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 3 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, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "ipc-legacy-impl" + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libsysprof/sysprof-kallsyms.h" +#include "helpers.h" +#include "ipc-legacy-impl.h" + +struct _IpcLegacyImpl +{ + IpcLegacySysprof2Skeleton parent; +}; + +enum { + ACTIVITY, + N_SIGNALS +}; + +static guint signals [N_SIGNALS]; + +static gboolean +ipc_legacy_impl_handle_perf_event_open (IpcLegacySysprof2 *service, + GDBusMethodInvocation *invocation, + GVariant *options, + gint32 pid, + gint32 cpu, + guint64 flags) +{ + gint out_fd = -1; + gint handle; + + g_assert (IPC_IS_LEGACY_IMPL (service)); + g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); + + g_message ("LEGACY: PerfEventOpen(pid=%d, cpu=%d)", pid, cpu); + + errno = 0; + if (!helpers_perf_event_open (options, pid, cpu, -1, flags, &out_fd)) + { + g_dbus_method_invocation_return_error (g_steal_pointer (&invocation), + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Failed to create perf counter: %s", + g_strerror (errno)); + } + else + { + g_autoptr(GUnixFDList) out_fd_list = g_unix_fd_list_new (); + g_autoptr(GError) error = NULL; + + if (-1 == (handle = g_unix_fd_list_append (out_fd_list, out_fd, &error))) + { + g_dbus_method_invocation_return_error (g_steal_pointer (&invocation), + G_DBUS_ERROR, + G_DBUS_ERROR_LIMITS_EXCEEDED, + "Failed to create file-descriptor for reply"); + } + else + { + g_dbus_method_invocation_return_value_with_unix_fd_list (g_steal_pointer (&invocation), + g_variant_new ("(h)", handle), + out_fd_list); + } + } + + if (out_fd != -1) + close (out_fd); + + return TRUE; +} + +static gboolean +ipc_legacy_impl_handle_get_kernal_symbols (IpcLegacySysprof2 *service, + GDBusMethodInvocation *invocation) +{ + g_autoptr(SysprofKallsyms) kallsyms = NULL; + GVariantBuilder builder; + const gchar *name; + guint64 addr; + guint8 type; + + g_assert (IPC_IS_LEGACY_IMPL (service)); + g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); + + g_message ("LEGACY: GetKernelSymbols()"); + + if (!(kallsyms = sysprof_kallsyms_new ("/proc/kallsyms"))) + { + g_dbus_method_invocation_return_error (g_steal_pointer (&invocation), + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Failed to create parse kallsyms"); + return TRUE; + } + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(tys)")); + while (sysprof_kallsyms_next (kallsyms, &name, &addr, &type)) + g_variant_builder_add (&builder, "(tys)", addr, type, name); + + ipc_legacy_sysprof2_complete_get_kernel_symbols (service, + g_steal_pointer (&invocation), + g_variant_builder_end (&builder)); + + return TRUE; +} + +static gboolean +ipc_legacy_impl_g_authorize_method (GDBusInterfaceSkeleton *skeleton, + GDBusMethodInvocation *invocation) +{ + PolkitAuthorizationResult *res = NULL; + PolkitAuthority *authority = NULL; + PolkitSubject *subject = NULL; + const gchar *peer_name; + gboolean ret = TRUE; + + g_assert (IPC_IS_LEGACY_IMPL (skeleton)); + g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); + + g_signal_emit (skeleton, signals [ACTIVITY], 0); + + peer_name = g_dbus_method_invocation_get_sender (invocation); + + if (!(authority = polkit_authority_get_sync (NULL, NULL)) || + !(subject = polkit_system_bus_name_new (peer_name)) || + !(res = polkit_authority_check_authorization_sync (authority, + POLKIT_SUBJECT (subject), + "org.gnome.sysprof3.profile", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + NULL)) || + !polkit_authorization_result_get_is_authorized (res)) + { + g_dbus_method_invocation_return_error (g_steal_pointer (&invocation), + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Not authorized to make request"); + ret = FALSE; + } + + g_clear_object (&authority); + g_clear_object (&subject); + g_clear_object (&res); + + return ret; +} + +static void +sysprof2_iface_init (IpcLegacySysprof2Iface *iface) +{ + iface->handle_perf_event_open = ipc_legacy_impl_handle_perf_event_open; + iface->handle_get_kernel_symbols = ipc_legacy_impl_handle_get_kernal_symbols; +} + +G_DEFINE_TYPE_WITH_CODE (IpcLegacyImpl, ipc_legacy_impl, IPC_LEGACY_TYPE_SYSPROF2_SKELETON, + G_IMPLEMENT_INTERFACE (IPC_LEGACY_TYPE_SYSPROF2, sysprof2_iface_init)) + +static void +ipc_legacy_impl_class_init (IpcLegacyImplClass *klass) +{ + GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + + skeleton_class->g_authorize_method = ipc_legacy_impl_g_authorize_method; + + signals [ACTIVITY] = + g_signal_new ("activity", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); +} + +static void +ipc_legacy_impl_init (IpcLegacyImpl *self) +{ + g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (self), + G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); +} + +IpcLegacySysprof2 * +ipc_legacy_impl_new (void) +{ + return g_object_new (IPC_TYPE_LEGACY_IMPL, NULL); +} diff --git a/src/sysprofd/ipc-legacy-impl.h b/src/sysprofd/ipc-legacy-impl.h new file mode 100644 index 00000000..e0c2c673 --- /dev/null +++ b/src/sysprofd/ipc-legacy-impl.h @@ -0,0 +1,33 @@ +/* ipc-legacy-impl.h + * + * Copyright 2019 Christian Hergert + * + * 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 3 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, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "ipc-legacy.h" + +G_BEGIN_DECLS + +#define IPC_TYPE_LEGACY_IMPL (ipc_legacy_impl_get_type()) + +G_DECLARE_FINAL_TYPE (IpcLegacyImpl, ipc_legacy_impl, IPC, LEGACY_IMPL, IpcLegacySysprof2Skeleton) + +IpcLegacySysprof2 *ipc_legacy_impl_new (void); + +G_END_DECLS diff --git a/src/sysprofd/meson.build b/src/sysprofd/meson.build index 2478e41e..f605c265 100644 --- a/src/sysprofd/meson.build +++ b/src/sysprofd/meson.build @@ -1,9 +1,12 @@ if get_option('with_sysprofd') == 'bundled' sysprofd_sources = [ + '../libsysprof/sysprof-kallsyms.c', 'sysprofd.c', + 'ipc-legacy-impl.c', 'ipc-service-impl.c', helpers_sources, + ipc_legacy_src, ipc_service_src, ] @@ -27,6 +30,15 @@ sysprofd = executable('sysprofd', sysprofd_sources, sysprofdconf = configuration_data() sysprofdconf.set('sysprofdprivdir', pkglibexecdir) +systemdunitdir = get_option('systemdunitdir') +if systemdunitdir == '' + systemdunitdir = dependency('systemd').get_pkgconfig_variable('systemdsystemunitdir') +endif + +# +# For org.gnome.Sysprof3 +# + configure_file( input: 'org.gnome.Sysprof3.service.in', output: 'org.gnome.Sysprof3.service', @@ -41,10 +53,6 @@ configure_file( install_dir: join_paths(datadir, 'dbus-1/system.d'), ) -systemdunitdir = get_option('systemdunitdir') -if systemdunitdir == '' - systemdunitdir = dependency('systemd').get_pkgconfig_variable('systemdsystemunitdir') -endif configure_file( input: 'sysprof3.service.in', output: 'sysprof3.service', @@ -60,4 +68,29 @@ i18n.merge_file( install_dir: join_paths(datadir, 'polkit-1/actions'), ) +# +# For org.gnome.Sysprof2 Compatibility +# + +configure_file( + input: 'org.gnome.Sysprof2.service.in', + output: 'org.gnome.Sysprof2.service', + configuration: sysprofdconf, + install_dir: join_paths(datadir, 'dbus-1/system-services'), +) + +configure_file( + input: 'org.gnome.Sysprof2.conf.in', + output: 'org.gnome.Sysprof2.conf', + configuration: sysprofdconf, + install_dir: join_paths(datadir, 'dbus-1/system.d'), +) + +configure_file( + input: 'sysprof2.service.in', + output: 'sysprof2.service', + configuration: sysprofdconf, + install_dir: systemdunitdir, +) + endif diff --git a/src/sysprofd/org.gnome.Sysprof2.conf.in b/src/sysprofd/org.gnome.Sysprof2.conf.in new file mode 100644 index 00000000..c46c7a6b --- /dev/null +++ b/src/sysprofd/org.gnome.Sysprof2.conf.in @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/src/sysprofd/org.gnome.Sysprof2.service.in b/src/sysprofd/org.gnome.Sysprof2.service.in new file mode 100644 index 00000000..3455b650 --- /dev/null +++ b/src/sysprofd/org.gnome.Sysprof2.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=org.gnome.Sysprof2 +Exec=@sysprofdprivdir@/sysprofd +User=root +SystemdService=sysprof2.service diff --git a/src/sysprofd/org.gnome.sysprof3.policy.in b/src/sysprofd/org.gnome.sysprof3.policy.in index e29ed2aa..8271cff4 100644 --- a/src/sysprofd/org.gnome.sysprof3.policy.in +++ b/src/sysprofd/org.gnome.sysprof3.policy.in @@ -8,6 +8,7 @@ The sysprof Project https://wiki.gnome.org/Apps/Sysprof org.gnome.Sysprof-symbolic + Profile the system Authentication is required to profile the system. @@ -16,5 +17,32 @@ auth_admin_keep auth_admin_keep + org.gnome.sysprof2.perf-event-open + org.gnome.sysprof2.get-kernel-symbols + + + + Open a perf event stream + Authentication is required to access system performance counters. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.gnome.sysprof3.profile + org.gnome.sysprof2.get-kernel-symbols + + + Get a list of kernel symbols and their address + Authentication is required to access Linux kernel information. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.gnome.sysprof3.profile + org.gnome.sysprof2.perf-event-open + + diff --git a/src/sysprofd/sysprof2.service.in b/src/sysprofd/sysprof2.service.in new file mode 100644 index 00000000..9ee46328 --- /dev/null +++ b/src/sysprofd/sysprof2.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Sysprof Daemon + +[Service] +Type=dbus +BusName=org.gnome.Sysprof2 +ExecStart=@sysprofdprivdir@/sysprofd + diff --git a/src/sysprofd/sysprofd.c b/src/sysprofd/sysprofd.c index 316d53b8..e084a17d 100644 --- a/src/sysprofd/sysprofd.c +++ b/src/sysprofd/sysprofd.c @@ -25,17 +25,21 @@ #include #include +#include "ipc-legacy.h" #include "ipc-service.h" + +#include "ipc-legacy-impl.h" #include "ipc-service-impl.h" -#define BUS_NAME "org.gnome.Sysprof3" -#define OBJECT_PATH "/org/gnome/Sysprof3" +#define V2_PATH "/org/gnome/Sysprof2" +#define V3_PATH "/org/gnome/Sysprof3" #define NAME_ACQUIRE_DELAY_SECS 3 #define INACTIVITY_TIMEOUT_SECS 120 -static GMainLoop *main_loop; -static gboolean name_acquired; -static gint exit_status = EXIT_SUCCESS; +static const gchar *bus_names[] = { "org.gnome.Sysprof3", "org.gnome.Sysprof2" }; +static GMainLoop *main_loop; +static gboolean name_acquired; +static gint exit_status = EXIT_SUCCESS; static guint inactivity; static G_LOCK_DEFINE (activity); @@ -49,8 +53,8 @@ inactivity_cb (gpointer data) } static void -activity_cb (IpcService *service, - gpointer user_data) +activity_cb (GObject *object, + gpointer user_data) { G_LOCK (activity); if (inactivity) @@ -75,23 +79,28 @@ name_lost_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { - /* Exit if we lost the name */ - if (g_strcmp0 (name, BUS_NAME) == 0) + /* Exit if we lost one of our bus names */ + for (guint i = 0; i < G_N_ELEMENTS (bus_names); i++) { - g_message ("Lost Bus Name: %s, exiting.", name); - name_acquired = FALSE; - g_main_loop_quit (main_loop); + if (g_strcmp0 (name, bus_names[i]) == 0) + { + g_message ("Lost Bus Name: %s, exiting.", bus_names[i]); + name_acquired = FALSE; + g_main_loop_quit (main_loop); + } } } static gboolean wait_for_acquire_timeout_cb (gpointer data) { + const gchar *bus_name = data; + if (!name_acquired) { exit_status = EXIT_FAILURE; - g_critical ("Failed to acquire name on bus after %d seconds, exiting.", - NAME_ACQUIRE_DELAY_SECS); + g_critical ("Failed to acquire %s on bus after %d seconds, exiting.", + bus_name, NAME_ACQUIRE_DELAY_SECS); g_main_loop_quit (main_loop); } @@ -113,31 +122,34 @@ main (gint argc, if ((bus = g_bus_get_sync (bus_type, NULL, &error))) { - g_autoptr(IpcService) service = ipc_service_impl_new (); + g_autoptr(IpcLegacySysprof2) v2_service = ipc_legacy_impl_new (); + g_autoptr(IpcService) v3_service = ipc_service_impl_new (); - g_signal_connect (service, - "activity", - G_CALLBACK (activity_cb), - NULL); + g_signal_connect (v3_service, "activity", G_CALLBACK (activity_cb), NULL); + g_signal_connect (v2_service, "activity", G_CALLBACK (activity_cb), NULL); - activity_cb (service, NULL); + activity_cb (NULL, NULL); - if (g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service), - bus, - OBJECT_PATH, - &error)) + if (g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (v3_service), bus, V3_PATH, &error) && + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (v2_service), bus, V2_PATH, &error)) { - g_bus_own_name_on_connection (bus, - BUS_NAME, - (G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | - G_BUS_NAME_OWNER_FLAGS_REPLACE), - name_acquired_cb, - name_lost_cb, - NULL, - NULL); - g_timeout_add_seconds (NAME_ACQUIRE_DELAY_SECS, - wait_for_acquire_timeout_cb, - NULL); + for (guint i = 0; i < G_N_ELEMENTS (bus_names); i++) + { + g_bus_own_name_on_connection (bus, + bus_names[i], + (G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE), + name_acquired_cb, + name_lost_cb, + NULL, + NULL); + g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + NAME_ACQUIRE_DELAY_SECS, + wait_for_acquire_timeout_cb, + g_strdup (bus_names[i]), + g_free); + } + g_main_loop_run (main_loop); g_main_loop_unref (main_loop);