tree: start on massive tree refactor

The big thing going on here is that we are going to split up the libraries
a bit better, and remove GObject from the capture library. The libsysprof
library will bring in the capture library statically, so we can export the
symbols we want.

Eventually, we will bump the version to sysprof-3, but not yet.
This commit is contained in:
Christian Hergert
2019-05-07 20:52:05 -07:00
parent 5323cffdb3
commit 1708ad1b48
193 changed files with 1400 additions and 1136 deletions

60
src/sysprofd/meson.build Normal file
View File

@ -0,0 +1,60 @@
if get_option('with_sysprofd') == 'bundled'
sysprofd_sources = [
'sysprofd.c',
'sd-bus-helper.c',
'sd-bus-helper.h',
]
pkglibexecdir = join_paths(get_option('prefix'), get_option('libexecdir'))
sysprofd_deps = [
dependency('libsystemd', version: '>=222'),
dependency('glib-2.0', version: glib_req_version),
libshared_dep,
]
sysprofd = executable('sysprofd', sysprofd_sources,
dependencies: sysprofd_deps,
install: true,
install_dir: pkglibexecdir,
pie: true,
)
sysprofdconf = configuration_data()
sysprofdconf.set('sysprofdprivdir', pkglibexecdir)
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'),
)
systemdunitdir = get_option('systemdunitdir')
if systemdunitdir == ''
systemdunitdir = dependency('systemd').get_pkgconfig_variable('systemdsystemunitdir')
endif
configure_file(
input: 'sysprof2.service.in',
output: 'sysprof2.service',
configuration: sysprofdconf,
install_dir: systemdunitdir,
)
i18n.merge_file(
input: 'org.gnome.sysprof2.policy.in',
output: 'org.gnome.sysprof2.policy',
po_dir: podir,
install: true,
install_dir: join_paths(datadir, 'polkit-1/actions'),
)
endif

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="org.gnome.Sysprof2"/>
<allow send_destination="org.gnome.Sysprof2"/>
<allow receive_sender="org.gnome.Sysprof2"/>
</policy>
<!-- Anyone can send messages to the owner of org.gnome.Sysprof2 -->
<policy context="default">
<allow send_destination="org.gnome.Sysprof2"/>
<allow receive_sender="org.gnome.Sysprof2"/>
</policy>
</busconfig>

View File

@ -0,0 +1,5 @@
[D-BUS Service]
Name=org.gnome.Sysprof2
Exec=@sysprofdprivdir@/sysprofd
User=root
SystemdService=sysprof2.service

View File

@ -0,0 +1,39 @@
<!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>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>The sysprof Project</vendor>
<vendor_url>https://wiki.gnome.org/Apps/Sysprof</vendor_url>
<icon_name>sysprof</icon_name>
<action id="org.gnome.sysprof2.perf-event-open">
<description>Open a perf event stream</description>
<message>Authentication is required to access system performance counters.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.imply">org.gnome.sysprof2.get-kernel-symbols</annotate>
</action>
<action id="org.gnome.sysprof2.get-kernel-symbols">
<description>Get a list of kernel symbols and their address</description>
<message>Authentication is required to access Linux kernel information.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.imply">org.gnome.sysprof2.perf-event-open</annotate>
</action>
</policyconfig>

View File

@ -0,0 +1,188 @@
/***
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;
}

View File

@ -0,0 +1,36 @@
/***
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 */

View File

@ -0,0 +1,8 @@
[Unit]
Description=Sysprof Daemon
[Service]
Type=dbus
BusName=org.gnome.Sysprof2
ExecStart=@sysprofdprivdir@/sysprofd

425
src/sysprofd/sysprofd.c Normal file
View File

@ -0,0 +1,425 @@
/* sysprofd.c
*
* Copyright 2016 Christian Hergert <christian@hergert.me>
*
* 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/>.
*/
#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 "sd-bus-helper.h"
#include "sp-kallsyms.h"
#define BUS_TIMEOUT_USEC (1000000L * 10L)
#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)
{
g_autoptr(SpKallsyms) 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 = sp_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 (sp_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;
}
static int
_perf_event_open (struct perf_event_attr *attr,
pid_t pid,
int cpu,
int group_fd,
unsigned long flags)
{
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);
}
static int
sysprofd_perf_event_open (sd_bus_message *msg,
void *user_data,
sd_bus_error *error)
{
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 (;;)
{
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;
}
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;
}
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[])
{
sd_bus_slot *slot = NULL;
sd_bus *bus = NULL;
int r;
/* 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;
}
/* 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;
}
/* 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;
}
for (;;)
{
/* Process requests */
r = sd_bus_process (bus, NULL);
if (r < 0)
{
fprintf (stderr,
"Failed to process bus: %s\n",
strerror (-r));
goto failure;
}
/* 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);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}