[sysprof/wip/chergert/sysprof-3] sysprofd: add API to get a FD for a file in proc



commit 64fdd9f763bc547f0167f7e8a83b8caf6626d3cc
Author: Christian Hergert <chergert redhat com>
Date:   Sun May 12 16:06:02 2019 -0700

    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).

 src/helpers.c                      | 20 +++++++++++++++
 src/helpers.h                      |  2 ++
 src/libsysprof/sysprof-helpers.c   | 50 ++++++++++++++++++++++++++++++++++++++
 src/libsysprof/sysprof-helpers.h   |  5 ++++
 src/org.gnome.Sysprof3.Service.xml | 20 ++++++++++++++-
 src/sysprofd/ipc-service-impl.c    | 46 +++++++++++++++++++++++++++++++++++
 6 files changed, 142 insertions(+), 1 deletion(-)
---
diff --git a/src/helpers.c b/src/helpers.c
index b806466..ebba685 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -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)
 {
diff --git a/src/helpers.h b/src/helpers.h
index 430a4e2..212106b 100644
--- a/src/helpers.h
+++ b/src/helpers.h
@@ -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
diff --git a/src/libsysprof/sysprof-helpers.c b/src/libsysprof/sysprof-helpers.c
index f02c63e..d577286 100644
--- a/src/libsysprof/sysprof-helpers.c
+++ b/src/libsysprof/sysprof-helpers.c
@@ -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,
diff --git a/src/libsysprof/sysprof-helpers.h b/src/libsysprof/sysprof-helpers.h
index 517ba49..99aae85 100644
--- a/src/libsysprof/sysprof-helpers.h
+++ b/src/libsysprof/sysprof-helpers.h
@@ -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,
diff --git a/src/org.gnome.Sysprof3.Service.xml b/src/org.gnome.Sysprof3.Service.xml
index d39f68c..fad593a 100644
--- a/src/org.gnome.Sysprof3.Service.xml
+++ b/src/org.gnome.Sysprof3.Service.xml
@@ -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:
 
diff --git a/src/sysprofd/ipc-service-impl.c b/src/sysprofd/ipc-service-impl.c
index b1be108..163794e 100644
--- a/src/sysprofd/ipc-service-impl.c
+++ b/src/sysprofd/ipc-service-impl.c
@@ -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;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]