mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
sysprofd: start on port to GDBusConnection
This will help us reduce a lot of code and eventually make some things more asynchronous to allow for interactive authorization.
This commit is contained in:
135
src/sysprofd/ipc-service-impl.c
Normal file
135
src/sysprofd/ipc-service-impl.c
Normal file
@ -0,0 +1,135 @@
|
||||
/* ipc-service-impl.c
|
||||
*
|
||||
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "ipc-service-impl"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ipc-service-impl.h"
|
||||
|
||||
struct _IpcServiceImpl
|
||||
{
|
||||
IpcServiceSkeleton parent_instance;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
ipc_service_impl_handle_list_processes (IpcService *service,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
g_autoptr(GDir) dir = NULL;
|
||||
g_autoptr(GArray) pids = NULL;
|
||||
const gchar *name;
|
||||
|
||||
g_assert (IPC_IS_SERVICE_IMPL (service));
|
||||
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
||||
|
||||
if (!(dir = g_dir_open ("/proc/", 0, NULL)))
|
||||
{
|
||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||
G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_FILE_NOT_FOUND,
|
||||
"Failed to access /proc");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pids = g_array_new (FALSE, FALSE, sizeof (gint32));
|
||||
|
||||
while ((name = g_dir_read_name (dir)))
|
||||
{
|
||||
if (g_ascii_isalnum (*name))
|
||||
{
|
||||
gchar *endptr = NULL;
|
||||
gint64 val = g_ascii_strtoll (name, &endptr, 10);
|
||||
|
||||
if (endptr != NULL && *endptr == 0 && val < G_MAXINT && val >= 0)
|
||||
{
|
||||
gint32 v32 = val;
|
||||
g_array_append_val (pids, v32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ipc_service_complete_list_processes (service,
|
||||
g_steal_pointer (&invocation),
|
||||
g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
|
||||
pids->data,
|
||||
pids->len,
|
||||
sizeof (gint32)));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ipc_service_impl_handle_get_proc_file (IpcService *service,
|
||||
GDBusMethodInvocation *invocation,
|
||||
const gchar *path)
|
||||
{
|
||||
g_autofree gchar *canon = NULL;
|
||||
g_autofree gchar *contents = NULL;
|
||||
gsize len;
|
||||
|
||||
g_assert (IPC_IS_SERVICE_IMPL (service));
|
||||
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
||||
|
||||
canon = g_canonicalize_filename (path, "/proc/");
|
||||
|
||||
if (!g_str_has_prefix (canon, "/proc/"))
|
||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||
G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_ACCESS_DENIED,
|
||||
"File is not within /proc/");
|
||||
else if (!g_file_get_contents (canon, &contents, &len, NULL))
|
||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||
G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_FILE_NOT_FOUND,
|
||||
"Failed to locate the file");
|
||||
else
|
||||
ipc_service_complete_get_proc_file (service,
|
||||
g_steal_pointer (&invocation),
|
||||
contents);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
init_service_iface (IpcServiceIface *iface)
|
||||
{
|
||||
iface->handle_list_processes = ipc_service_impl_handle_list_processes;
|
||||
iface->handle_get_proc_file = ipc_service_impl_handle_get_proc_file;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (IpcServiceImpl, ipc_service_impl, IPC_TYPE_SERVICE_SKELETON,
|
||||
G_IMPLEMENT_INTERFACE (IPC_TYPE_SERVICE, init_service_iface))
|
||||
|
||||
static void
|
||||
ipc_service_impl_class_init (IpcServiceImplClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
ipc_service_impl_init (IpcServiceImpl *self)
|
||||
{
|
||||
}
|
||||
|
||||
IpcService *
|
||||
ipc_service_impl_new (void)
|
||||
{
|
||||
return g_object_new (IPC_TYPE_SERVICE_IMPL, NULL);
|
||||
}
|
||||
33
src/sysprofd/ipc-service-impl.h
Normal file
33
src/sysprofd/ipc-service-impl.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* ipc-service-impl.h
|
||||
*
|
||||
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ipc-service.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define IPC_TYPE_SERVICE_IMPL (ipc_service_impl_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (IpcServiceImpl, ipc_service_impl, IPC, SERVICE_IMPL, IpcServiceSkeleton)
|
||||
|
||||
IpcService *ipc_service_impl_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
@ -1,10 +1,15 @@
|
||||
if get_option('with_sysprofd') == 'bundled'
|
||||
|
||||
ipc_service_src = gnome.gdbus_codegen('ipc-service',
|
||||
sources: 'org.gnome.Sysprof3.Service.xml',
|
||||
interface_prefix: 'org.gnome.Sysprof3.',
|
||||
namespace: 'Ipc',
|
||||
)
|
||||
|
||||
sysprofd_sources = [
|
||||
'sysprofd.c',
|
||||
'sd-bus-helper.c',
|
||||
'sd-bus-helper.h',
|
||||
'../libsysprof/sysprof-kallsyms.c',
|
||||
'ipc-service-impl.c',
|
||||
ipc_service_src,
|
||||
]
|
||||
|
||||
pkglibexecdir = join_paths(get_option('prefix'), get_option('libexecdir'))
|
||||
@ -12,6 +17,8 @@ pkglibexecdir = join_paths(get_option('prefix'), get_option('libexecdir'))
|
||||
sysprofd_deps = [
|
||||
dependency('libsystemd', version: '>=222'),
|
||||
dependency('glib-2.0', version: glib_req_version),
|
||||
dependency('gio-2.0', version: glib_req_version),
|
||||
dependency('gio-unix-2.0', version: glib_req_version),
|
||||
]
|
||||
|
||||
sysprofd = executable('sysprofd', sysprofd_sources,
|
||||
@ -19,9 +26,6 @@ sysprofd = executable('sysprofd', sysprofd_sources,
|
||||
install: true,
|
||||
install_dir: pkglibexecdir,
|
||||
pie: true,
|
||||
include_directories: [include_directories('.'),
|
||||
'../libsysprof',
|
||||
libsysprof_capture_include_dirs],
|
||||
)
|
||||
|
||||
sysprofdconf = configuration_data()
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.gnome.Sysprof2">
|
||||
<!--
|
||||
PerfEventOpen:
|
||||
@options: key-value pair of attributes for the perf_event_open() syscall.
|
||||
@pid: the process id to monitor, or -1 for system-wide.
|
||||
@cpu: affinity to cpu.
|
||||
@flags: flags for perf_event_open() syscall.
|
||||
@perf_stream_fd: (out): A fd to communicate with perf.
|
||||
|
||||
Performs the perf_event_open() syscall with elevated privileges and passes
|
||||
the resulting fd back to the calling process.
|
||||
-->
|
||||
<method name="PerfEventOpen">
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
<arg name="pid" type="i" direction="in"/>
|
||||
<arg name="cpu" type="i" direction="in"/>
|
||||
<arg name="flags" type="t" direction="in"/>
|
||||
<arg name="perf_stream_fd" type="h" direction="out"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
GetKernelSymbols:
|
||||
|
||||
Loads the name, type, and address of Linux kernel symbols. This is useful
|
||||
if your system does not provide access to /proc/kallsyms to non-root users.
|
||||
|
||||
Returns: an array of (tys) tuples. t is the address, y is the type, and s
|
||||
is the name.
|
||||
|
||||
Since: 3.28
|
||||
-->
|
||||
<method name="GetKernelSymbols">
|
||||
<arg name="symbols" type="a(tys)" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
54
src/sysprofd/org.gnome.Sysprof3.Service.xml
Normal file
54
src/sysprofd/org.gnome.Sysprof3.Service.xml
Normal file
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.gnome.Sysprof3.Service">
|
||||
<!--
|
||||
PerfEventOpen:
|
||||
@options: key-value pair of attributes for the perf_event_open() syscall.
|
||||
@pid: the process id to monitor, or -1 for system-wide.
|
||||
@cpu: affinity to cpu.
|
||||
@flags: flags for perf_event_open() syscall.
|
||||
@perf_stream_fd: (out): A fd to communicate with perf.
|
||||
|
||||
Performs the perf_event_open() syscall with elevated privileges and passes
|
||||
the resulting fd back to the calling process.
|
||||
-->
|
||||
<method name="PerfEventOpen">
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
<arg name="pid" type="i" direction="in"/>
|
||||
<arg name="cpu" type="i" direction="in"/>
|
||||
<arg name="flags" type="t" direction="in"/>
|
||||
<arg name="perf_stream_fd" type="h" direction="out"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
GetProcFile:
|
||||
@path: the path including "/proc/" prefix.
|
||||
|
||||
Gets a file from /proc.
|
||||
|
||||
The file must be a C-string (no embedded NUL bytes) but need not be UTF-8.
|
||||
|
||||
Returns: an array of bytes.
|
||||
|
||||
Since: 3.34
|
||||
-->
|
||||
<method name="GetProcFile">
|
||||
<arg name="path" type="ay" direction="in"/>
|
||||
<arg name="contents" type="ay" direction="out"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ListProcesses:
|
||||
|
||||
List the processes on the system.
|
||||
|
||||
Returns: an array of 32-bit integers
|
||||
|
||||
Since: 3.34
|
||||
-->
|
||||
<method name="ListProcesses">
|
||||
<arg name="processes" type="ai" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
@ -1,188 +0,0 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sd-bus-helper.h"
|
||||
|
||||
/*
|
||||
* Various macros to simplify lifing code from sd-bus.
|
||||
*/
|
||||
#define assert_return(expr,val) do { if (!(expr)) return (val); } while (0)
|
||||
#define _cleanup_(f) __attribute__((cleanup(f)))
|
||||
#define STRV_FOREACH_PAIR(x, y, l) \
|
||||
for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
|
||||
|
||||
/*
|
||||
* To support systemd 222, we need a couple helpers that were added
|
||||
* in 229. If we update code from systemd in the future, we can probably
|
||||
* drop these helpres.
|
||||
*/
|
||||
|
||||
static void
|
||||
_sd_bus_message_unrefp (sd_bus_message **m)
|
||||
{
|
||||
if (m && *m)
|
||||
{
|
||||
sd_bus_message_unref (*m);
|
||||
*m = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_sd_bus_creds_unrefp (sd_bus_creds **c)
|
||||
{
|
||||
if (c && *c)
|
||||
{
|
||||
sd_bus_creds_unref (*c);
|
||||
*c = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin verbatim code from systemd. Please try to keep this in sync until
|
||||
* systemd exposes helpers for polkit integration.
|
||||
*/
|
||||
static int
|
||||
check_good_user (sd_bus_message *m,
|
||||
uid_t good_user)
|
||||
{
|
||||
_cleanup_(_sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||
uid_t sender_uid;
|
||||
int r;
|
||||
|
||||
assert (m);
|
||||
|
||||
if (good_user == UID_INVALID)
|
||||
return 0;
|
||||
|
||||
r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Don't trust augmented credentials for authorization */
|
||||
assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
|
||||
|
||||
r = sd_bus_creds_get_euid(creds, &sender_uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sender_uid == good_user;
|
||||
}
|
||||
|
||||
int
|
||||
bus_test_polkit (sd_bus_message *call,
|
||||
int capability,
|
||||
const char *action,
|
||||
const char **details,
|
||||
uid_t good_user,
|
||||
bool *_challenge,
|
||||
sd_bus_error *e)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert (call);
|
||||
assert (action);
|
||||
|
||||
/* Tests non-interactively! */
|
||||
|
||||
r = check_good_user(call, good_user);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_query_sender_privilege(call, capability);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
return 1;
|
||||
else {
|
||||
_cleanup_(_sd_bus_message_unrefp) sd_bus_message *request = NULL;
|
||||
_cleanup_(_sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
int authorized = false, challenge = false;
|
||||
const char *sender, **k, **v;
|
||||
|
||||
sender = sd_bus_message_get_sender(call);
|
||||
if (!sender)
|
||||
return -EBADMSG;
|
||||
|
||||
r = sd_bus_message_new_method_call(sd_bus_message_get_bus (call),
|
||||
&request,
|
||||
"org.freedesktop.PolicyKit1",
|
||||
"/org/freedesktop/PolicyKit1/Authority",
|
||||
"org.freedesktop.PolicyKit1.Authority",
|
||||
"CheckAuthorization");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(request,
|
||||
"(sa{sv})s",
|
||||
"system-bus-name", 1, "name", "s", sender,
|
||||
action);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(request, 'a', "{ss}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH_PAIR(k, v, details) {
|
||||
r = sd_bus_message_append(request, "{ss}", *k, *v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(request);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(request, "us", 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_call(sd_bus_message_get_bus(call), request, 0, e, &reply);
|
||||
if (r < 0) {
|
||||
/* Treat no PK available as access denied */
|
||||
if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
|
||||
sd_bus_error_free(e);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (authorized)
|
||||
return 1;
|
||||
|
||||
if (_challenge) {
|
||||
*_challenge = challenge;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifndef SD_BUS_HELPER_H
|
||||
#define SD_BUS_HELPER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#define UID_INVALID ((uid_t)-1)
|
||||
|
||||
int bus_test_polkit (sd_bus_message *call,
|
||||
int capability,
|
||||
const char *action,
|
||||
const char **details,
|
||||
uid_t good_user,
|
||||
bool *_challenge,
|
||||
sd_bus_error *e);
|
||||
|
||||
#endif /* SD_BUS_HELPER_H */
|
||||
@ -1,6 +1,6 @@
|
||||
/* sysprofd.c
|
||||
*
|
||||
* Copyright 2016 Christian Hergert <christian@hergert.me>
|
||||
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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
|
||||
@ -14,413 +14,110 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sysprofd"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "sd-bus-helper.h"
|
||||
#include "ipc-service.h"
|
||||
#include "ipc-service-impl.h"
|
||||
|
||||
#include "sysprof-kallsyms.h"
|
||||
#define BUS_NAME "org.gnome.Sysprof3"
|
||||
#define OBJECT_PATH "/org/gnome/Sysprof3"
|
||||
#define NAME_ACQUIRE_DELAY_SECS 3
|
||||
|
||||
#define BUS_TIMEOUT_USEC (1000000L * 10L)
|
||||
static GMainLoop *main_loop;
|
||||
static gboolean name_acquired;
|
||||
static gint exit_status = EXIT_SUCCESS;
|
||||
|
||||
#if 0
|
||||
#define GOTO(l) do { \
|
||||
fprintf (stderr, "GOTO: %s:%d: " #l "\n", __FUNCTION__, __LINE__); \
|
||||
goto l; \
|
||||
} while (0)
|
||||
#else
|
||||
#define GOTO(l) goto l
|
||||
#endif
|
||||
|
||||
static int
|
||||
sysprofd_get_kernel_symbols (sd_bus_message *msg,
|
||||
void *user_data,
|
||||
sd_bus_error *error)
|
||||
static void
|
||||
name_acquired_cb (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(SysprofKallsyms) kallsyms = NULL;
|
||||
sd_bus_message *reply = NULL;
|
||||
const gchar *name;
|
||||
guint64 addr;
|
||||
guint8 type;
|
||||
bool challenge = false;
|
||||
int r;
|
||||
|
||||
assert (msg);
|
||||
assert (error);
|
||||
|
||||
/* Authorize peer */
|
||||
r = bus_test_polkit (msg,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.gnome.sysprof2.get-kernel-symbols",
|
||||
NULL,
|
||||
UID_INVALID,
|
||||
&challenge,
|
||||
error);
|
||||
|
||||
if (r <= 0)
|
||||
fprintf (stderr, "GetKernelSymbols() Failure: %s\n", error->message);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r == 0)
|
||||
return -EACCES;
|
||||
|
||||
if (!(kallsyms = sysprof_kallsyms_new (NULL)))
|
||||
{
|
||||
sd_bus_error_set (error,
|
||||
SD_BUS_ERROR_FILE_NOT_FOUND,
|
||||
"Failed to open /proc/kallsyms");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
r = sd_bus_message_new_method_return (msg, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container (reply, 'a', "(tys)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while (sysprof_kallsyms_next (kallsyms, &name, &addr, &type))
|
||||
sd_bus_message_append (reply, "(tys)", addr, type, name);
|
||||
|
||||
r = sd_bus_message_close_container (reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_send (NULL, reply, NULL);
|
||||
sd_bus_message_unref (reply);
|
||||
|
||||
return r;
|
||||
g_message ("Acquired Bus Name: %s", name);
|
||||
name_acquired = TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
_perf_event_open (struct perf_event_attr *attr,
|
||||
pid_t pid,
|
||||
int cpu,
|
||||
int group_fd,
|
||||
unsigned long flags)
|
||||
static void
|
||||
name_lost_cb (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
assert (attr != NULL);
|
||||
|
||||
/* Quick sanity check */
|
||||
if (attr->sample_period < 100000 && attr->type != PERF_TYPE_TRACEPOINT)
|
||||
return -EINVAL;
|
||||
|
||||
return syscall (__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
|
||||
/* Exit if we lost the name */
|
||||
if (g_strcmp0 (name, BUS_NAME) == 0)
|
||||
{
|
||||
g_message ("Lost Bus Name: %s, exiting.", name);
|
||||
name_acquired = FALSE;
|
||||
g_main_loop_quit (main_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sysprofd_perf_event_open (sd_bus_message *msg,
|
||||
void *user_data,
|
||||
sd_bus_error *error)
|
||||
static gboolean
|
||||
wait_for_acquire_timeout_cb (gpointer data)
|
||||
{
|
||||
struct perf_event_attr attr = { 0 };
|
||||
sd_bus_message *reply = NULL;
|
||||
uint64_t flags = 0;
|
||||
int disabled = 0;
|
||||
int32_t wakeup_events = 149;
|
||||
int32_t cpu = -1;
|
||||
int32_t pid = -1;
|
||||
bool challenge = false;
|
||||
int32_t type = 0;
|
||||
uint64_t sample_period = 0;
|
||||
uint64_t sample_type = 0;
|
||||
uint64_t config = 0;
|
||||
int clockid = CLOCK_MONOTONIC_RAW;
|
||||
int comm = 0;
|
||||
int mmap_ = 0;
|
||||
int task = 0;
|
||||
int exclude_idle = 0;
|
||||
int fd = -1;
|
||||
int use_clockid = 0;
|
||||
int sample_id_all = 0;
|
||||
int r;
|
||||
|
||||
assert (msg);
|
||||
|
||||
r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_ARRAY, "{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (;;)
|
||||
if (!name_acquired)
|
||||
{
|
||||
const char *name = NULL;
|
||||
|
||||
r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_DICT_ENTRY, "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = sd_bus_message_read (msg, "s", &name);
|
||||
if (r < 0)
|
||||
goto cleanup;
|
||||
|
||||
r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_VARIANT, NULL);
|
||||
if (r < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp (name, "disabled") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &disabled);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "wakeup_events") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "u", &wakeup_events);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "sample_id_all") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &sample_id_all);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "clockid") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "i", &clockid);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "comm") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &comm);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "exclude_idle") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &exclude_idle);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "mmap") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &mmap_);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "config") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "t", &config);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "sample_period") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "t", &sample_period);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "sample_type") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "t", &sample_type);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "task") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &task);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "type") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "u", &type);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
else if (strcmp (name, "use_clockid") == 0)
|
||||
{
|
||||
r = sd_bus_message_read (msg, "b", &use_clockid);
|
||||
if (r < 0)
|
||||
GOTO (cleanup);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container (msg);
|
||||
if (r < 0)
|
||||
goto cleanup;
|
||||
|
||||
sd_bus_message_exit_container (msg);
|
||||
if (r < 0)
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
if (r < 0)
|
||||
return r;
|
||||
exit_status = EXIT_FAILURE;
|
||||
g_critical ("Failed to acquire name on bus after %d seconds, exiting.",
|
||||
NAME_ACQUIRE_DELAY_SECS);
|
||||
g_main_loop_quit (main_loop);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container (msg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read (msg, "iit", &pid, &cpu, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (pid < -1 || cpu < -1)
|
||||
return -EINVAL;
|
||||
|
||||
r = sd_bus_message_new_method_return (msg, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Authorize peer */
|
||||
r = bus_test_polkit (msg,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.gnome.sysprof2.perf-event-open",
|
||||
NULL,
|
||||
UID_INVALID,
|
||||
&challenge,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r == 0)
|
||||
return -EACCES;
|
||||
|
||||
attr.comm = !!comm;
|
||||
attr.config = config;
|
||||
attr.disabled = disabled;
|
||||
attr.exclude_idle = !!exclude_idle;
|
||||
attr.mmap = !!mmap_;
|
||||
attr.sample_id_all = sample_id_all;
|
||||
attr.sample_period = sample_period;
|
||||
attr.sample_type = sample_type;
|
||||
attr.task = !!task;
|
||||
attr.type = type;
|
||||
attr.wakeup_events = wakeup_events;
|
||||
|
||||
#ifdef HAVE_PERF_CLOCKID
|
||||
if (!use_clockid || clockid < 0)
|
||||
attr.clockid = CLOCK_MONOTONIC_RAW;
|
||||
else
|
||||
attr.clockid = clockid;
|
||||
attr.use_clockid = use_clockid;
|
||||
#endif
|
||||
|
||||
attr.size = sizeof attr;
|
||||
|
||||
fd = _perf_event_open (&attr, pid, cpu, -1, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to open perf event stream: %s\n",
|
||||
strerror (errno));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sd_bus_message_append_basic (reply, SD_BUS_TYPE_UNIX_FD, &fd);
|
||||
r = sd_bus_send (NULL, reply, NULL);
|
||||
sd_bus_message_unref (reply);
|
||||
|
||||
close (fd);
|
||||
|
||||
return r;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable sysprofd_vtable[] = {
|
||||
SD_BUS_VTABLE_START (0),
|
||||
SD_BUS_METHOD ("PerfEventOpen", "a{sv}iit", "h", sysprofd_perf_event_open, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD ("GetKernelSymbols", "", "a(tys)", sysprofd_get_kernel_symbols, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
gint
|
||||
main (gint argc,
|
||||
gchar *argv[])
|
||||
{
|
||||
sd_bus_slot *slot = NULL;
|
||||
sd_bus *bus = NULL;
|
||||
int r;
|
||||
g_autoptr(GDBusConnection) bus = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
GBusType bus_type = G_BUS_TYPE_SYSTEM;
|
||||
|
||||
/* Connect to the system bus */
|
||||
r = sd_bus_default_system (&bus);
|
||||
if (r < 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to connect to system bus: %s\n",
|
||||
strerror (-r));
|
||||
goto failure;
|
||||
}
|
||||
g_set_prgname ("sysprofd");
|
||||
g_set_application_name ("sysprofd");
|
||||
|
||||
/* Install our object */
|
||||
r = sd_bus_add_object_vtable (bus,
|
||||
&slot,
|
||||
"/org/gnome/Sysprof2",
|
||||
"org.gnome.Sysprof2",
|
||||
sysprofd_vtable,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to install object on bus: %s\n",
|
||||
strerror (-r));
|
||||
goto failure;
|
||||
}
|
||||
if (g_getenv ("SYSPROFD_USE_SESSION_BUS"))
|
||||
bus_type = G_BUS_TYPE_SESSION;
|
||||
|
||||
/* Request our well-known name on the bus */
|
||||
r = sd_bus_request_name (bus, "org.gnome.Sysprof2", 0);
|
||||
if (r < 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to register name on the bus: %s\n",
|
||||
strerror (-r));
|
||||
goto failure;
|
||||
}
|
||||
main_loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
for (;;)
|
||||
if ((bus = g_bus_get_sync (bus_type, NULL, &error)))
|
||||
{
|
||||
/* Process requests */
|
||||
r = sd_bus_process (bus, NULL);
|
||||
if (r < 0)
|
||||
g_autoptr(IpcService) service = ipc_service_impl_new ();
|
||||
|
||||
if (g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service),
|
||||
bus,
|
||||
OBJECT_PATH,
|
||||
&error))
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to process bus: %s\n",
|
||||
strerror (-r));
|
||||
goto failure;
|
||||
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);
|
||||
g_main_loop_run (main_loop);
|
||||
g_main_loop_unref (main_loop);
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
/* If we processed a request, continue processing */
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
/* Wait for the next request to process */
|
||||
r = sd_bus_wait (bus, BUS_TIMEOUT_USEC);
|
||||
if (r < 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to wait on bus: %s\n",
|
||||
strerror (-r));
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we timed out, we can expire, we will be auto-started by
|
||||
* systemd or dbus on the next activation request.
|
||||
*/
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
failure:
|
||||
sd_bus_slot_unref (slot);
|
||||
sd_bus_unref (bus);
|
||||
g_error ("Failed to setup system bus: %s", error->message);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
g_main_loop_unref (main_loop);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user