sysprofd: add API to get a FD for a file in proc

This is useful for optimized parsing of proc files such as necessary by
the memory source to seek() back to the start (instead of re-opening a
new file).
This commit is contained in:
Christian Hergert
2019-05-12 16:06:02 -07:00
parent 16bc6f970e
commit 9d72203687
6 changed files with 142 additions and 1 deletions

View File

@ -22,6 +22,7 @@
#include "config.h"
#include <fcntl.h>
#include <gio/gio.h>
#include <errno.h>
#ifdef __linux__
@ -281,6 +282,25 @@ helpers_get_proc_file (const gchar *path,
g_file_get_contents (canon, contents, len, NULL);
}
gboolean
helpers_get_proc_fd (const gchar *path,
gint *out_fd)
{
g_autofree gchar *canon = NULL;
g_autoptr(GFile) file = NULL;
g_assert (path != NULL);
g_assert (out_fd != NULL);
/* Canonicalize filename */
file = g_file_new_for_path (path);
canon = g_file_get_path (file);
return g_file_is_native (file) &&
(g_str_has_prefix (canon, "/proc/") || g_str_has_prefix (canon, "/sys/")) &&
-1 != (*out_fd = open (canon, O_RDONLY | O_CLOEXEC));
}
gboolean
helpers_can_see_pids (void)
{

View File

@ -39,5 +39,7 @@ gboolean helpers_perf_event_open (GVariant *options,
gboolean helpers_get_proc_file (const gchar *path,
gchar **contents,
gsize *len);
gboolean helpers_get_proc_fd (const gchar *path,
gint *out_fd);
G_END_DECLS

View File

@ -229,6 +229,56 @@ sysprof_helpers_list_processes_finish (SysprofHelpers *self,
return FALSE;
}
gboolean
sysprof_helpers_get_proc_fd (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
gint *out_fd,
GError **error)
{
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (out_fd != NULL, FALSE);
*out_fd = -1;
if (self->proxy != NULL)
{
g_autoptr(GVariant) reply = NULL;
g_autoptr(GUnixFDList) out_fd_list = NULL;
reply = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (self->proxy),
"GetProcFd",
g_variant_new ("(^ay)", path),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
NULL,
&out_fd_list,
cancellable,
error);
if (reply != NULL && out_fd_list != NULL)
{
gint handle = -1;
g_variant_get (reply, "(h)", &handle);
if (handle < g_unix_fd_list_get_length (out_fd_list))
{
*out_fd = g_unix_fd_list_get (out_fd_list, handle, error);
return *out_fd != -1;
}
}
}
if (!helpers_get_proc_fd (path, out_fd))
return FALSE;
g_clear_error (error);
return TRUE;
}
static void
sysprof_helpers_get_proc_file_cb (IpcService *service,
GAsyncResult *result,

View File

@ -54,6 +54,11 @@ gboolean sysprof_helpers_list_processes_finish (SysprofHelpers
gint32 **processes,
gsize *n_processes,
GError **error);
gboolean sysprof_helpers_get_proc_fd (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
gint *out_fd,
GError **error);
gboolean sysprof_helpers_get_proc_file (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,

View File

@ -27,7 +27,7 @@
GetProcFile:
@path: the path including "/proc/" prefix.
Gets a file from /proc.
Gets a file from /proc/ or /sys/.
The file must be a C-string (no embedded NUL bytes) but need not be UTF-8.
@ -40,6 +40,24 @@
<arg name="contents" type="ay" direction="out"/>
</method>
<!--
GetProcFd:
@path: the path within /proc or /sys
Like GetProcFile, but returns a FD for the file.
This is useful for situations where you need to seek() back to 0 to
optimize the parsing of the file contents.
Returns: a file-descriptor
Since: 3.34
-->
<method name="GetProcFd">
<arg name="path" type="ay" direction="in"/>
<arg name="fd" type="h" direction="out"/>
</method>
<!--
ListProcesses:

View File

@ -23,6 +23,7 @@
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <gio/gunixfdlist.h>
#include <polkit/polkit.h>
#include <string.h>
@ -88,6 +89,50 @@ ipc_service_impl_handle_get_proc_file (IpcService *service,
return TRUE;
}
static gboolean
ipc_service_impl_handle_get_proc_fd (IpcService *service,
GDBusMethodInvocation *invocation,
const gchar *path)
{
g_autoptr(GFile) file = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *canon = NULL;
g_assert (IPC_IS_SERVICE_IMPL (service));
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
file = g_file_new_for_path (path);
canon = g_file_get_path (file);
if (g_str_has_prefix (canon, "/proc/") || g_str_has_prefix (canon, "/sys/"))
{
gint fd = open (canon, O_RDONLY | O_CLOEXEC);
if (fd != -1)
{
g_autoptr(GUnixFDList) fd_list = g_unix_fd_list_new ();
gint handle = g_unix_fd_list_append (fd_list, fd, &error);
close (fd);
if (handle != -1)
{
g_dbus_method_invocation_return_value_with_unix_fd_list (g_steal_pointer (&invocation),
g_variant_new ("(h)", handle),
fd_list);
return TRUE;
}
}
}
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Failed to load proc fd");
return TRUE;
}
static gboolean
ipc_service_impl_handle_perf_event_open (IpcService *service,
GDBusMethodInvocation *invocation,
@ -343,6 +388,7 @@ 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;
iface->handle_get_proc_fd = ipc_service_impl_handle_get_proc_fd;
iface->handle_perf_event_open = ipc_service_impl_handle_perf_event_open;
iface->handle_get_process_info = ipc_service_impl_handle_get_process_info;
}