[gnome-online-accounts/wip/rishi/identity-kernel-keyring-notification: 5/5] keyring notification



commit e67ae8bf305ad7828723a50ad7a59037241eaa3c
Author: Debarshi Ray <debarshir gnome org>
Date:   Thu Jan 30 18:39:06 2020 +0100

    keyring notification

 configure.ac                                 |   2 +
 meson.build                                  |   4 +
 src/goaidentity/goakerberosidentitymanager.c | 204 +++++++++++++++++++++++----
 3 files changed, 182 insertions(+), 28 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 373816b1..6a2381bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -413,6 +413,8 @@ fi
 
 AM_CONDITIONAL(BUILD_IDENTITY_SERVICE, [test x$enable_fedora != xno || test x$enable_kerberos != xno])
 
+AC_CHECK_FUNCS(pipe2)
+
 # Optional timerfd support
 AC_MSG_CHECKING([for timerfd support])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
diff --git a/meson.build b/meson.build
index eb5eaa17..cc881c21 100644
--- a/meson.build
+++ b/meson.build
@@ -225,6 +225,10 @@ config_h.set_quoted('GOA_WINDOWS_LIVE_CLIENT_ID', windows_live_client_id)
 enable_windows_live = get_option('windows_live')
 config_h.set('GOA_WINDOWS_LIVE_ENABLED', enable_windows_live)
 
+if cc.has_function('pipe2')
+  config_h.set('HAVE_PIPE2', true)
+endif
+
 # Optional timerfd support
 timerfd_support_src = '''
   #include <sys/timerfd.h>
diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c
index 678db4c6..a3ba7c7c 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -18,16 +18,25 @@
 
 #include "config.h"
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
 #include "goakerberosidentitymanager.h"
 #include "goaidentitymanager.h"
 #include "goaidentitymanagererror.h"
 #include "goaidentitymanagerprivate.h"
 #include "goakerberosidentityinquiry.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <string.h>
+#include <unistd.h>
+#include <linux/keyctl.h>
+#include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
@@ -120,6 +129,11 @@ G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager,
                                                 initable_interface_init));
 #define FALLBACK_POLLING_INTERVAL 5
 
+enum
+{
+  WATCH_QUEUE_BUFFER_SIZE = 256
+};
+
 static Operation *
 operation_new (GoaKerberosIdentityManager *self,
                GCancellable               *cancellable,
@@ -174,6 +188,105 @@ operation_free (Operation *operation)
   g_slice_free (Operation, operation);
 }
 
+static GSource *
+goa_kerberos_identity_manager_keyring_source_new (GError **error)
+{
+  GSource *ret = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+#ifdef HAVE_PIPE2
+  {
+    g_autoptr (GInputStream) stream = NULL;
+    gint error_code;
+    gint fds[2] = { -1, -1 };
+    struct watch_notification_filter watch_queue_notification_filter = { 0 };
+    struct watch_notification_type_filter watch_queue_notification_types[1];
+
+    error_code = pipe2 (fds, O_NOTIFICATION_PIPE);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    close (fds[1]);
+
+    error_code = ioctl (fds[0], IOC_WATCH_QUEUE_SET_SIZE, WATCH_QUEUE_BUFFER_SIZE);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    watch_queue_notification_types[0].type = WATCH_TYPE_KEY_NOTIFY;
+    watch_queue_notification_types[0].info_filter = 0;
+    watch_queue_notification_types[0].info_mask = 0;
+    watch_queue_notification_types[0].subtype_filter[0] = G_MAXUINT;
+
+    watch_queue_notification_filter.nr_filters = 1;
+    watch_queue_notification_filter.filters = watch_queue_notification_types;
+
+    error_code = ioctl(fds[0], IOC_WATCH_QUEUE_SET_FILTER, &watch_queue_notification_filter);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    error_code = syscall (__NR_keyctl, KEYCTL_WATCH_KEY, KEY_SPEC_SESSION_KEYRING, fds[0], 0x01);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    error_code = syscall (__NR_keyctl, KEYCTL_WATCH_KEY, KEY_SPEC_USER_KEYRING, fds[0], 0x02);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    error_code = syscall (__NR_watch_devices, fds[0], 0x04, 0);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    stream = g_unix_input_stream_new (fds[0], TRUE);
+    ret = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (stream), NULL);
+  }
+#else
+  {
+    g_set_error_literal (error, G_UNIX_ERROR, 0, "pipe2(2) not supported");
+    goto out;
+  }
+#endif
+
+ out:
+  return ret;
+}
+
 typedef struct {
   GSourceFunc func;
   gboolean ret_val;
@@ -1320,6 +1433,13 @@ credentials_cache_file_monitor_changed (GFileMonitor               *monitor,
   schedule_refresh (self);
 }
 
+static gboolean
+credentials_cache_keyring_notification (GoaKerberosIdentityManager *self)
+{
+  schedule_refresh (self);
+  return G_SOURCE_CONTINUE;
+}
+
 static gboolean
 credentials_cache_polling_timeout (GoaKerberosIdentityManager *self)
 {
@@ -1335,7 +1455,6 @@ monitor_credentials_cache (GoaKerberosIdentityManager  *self,
   krb5_ccache default_cache;
   const char *cache_type;
   const char *cache_path;
-  GFileMonitor *monitor = NULL;
   krb5_error_code error_code;
   GError *monitoring_error = NULL;
   gboolean can_monitor = TRUE;
@@ -1359,13 +1478,6 @@ monitor_credentials_cache (GoaKerberosIdentityManager  *self,
   cache_type = krb5_cc_get_type (self->kerberos_context, default_cache);
   g_assert (cache_type != NULL);
 
-  if (strcmp (cache_type, "FILE") != 0 && strcmp (cache_type, "DIR") != 0)
-    {
-      g_warning ("GoaKerberosIdentityManager: Using polling for change notification for credential cache 
type '%s'",
-                 cache_type);
-      can_monitor = FALSE;
-    }
-
   g_free (self->credentials_cache_type);
   self->credentials_cache_type = g_strdup (cache_type);
 
@@ -1387,56 +1499,92 @@ monitor_credentials_cache (GoaKerberosIdentityManager  *self,
   if (cache_path[0] == ':')
     cache_path++;
 
-  if (can_monitor)
+  if (strcmp (cache_type, "FILE") == 0 || strcmp (cache_type, "DIR") == 0)
     {
-      GFile *file;
+      GFile *file = NULL;
+      GFileMonitor *monitor = NULL;
 
       file = g_file_new_for_path (cache_path);
 
-      monitoring_error = NULL;
       if (strcmp (cache_type, "FILE") == 0)
         {
-          monitor = g_file_monitor_file (file,
-                                         G_FILE_MONITOR_NONE,
-                                         NULL,
-                                         &monitoring_error);
+          monitoring_error = NULL;
+          monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &monitoring_error);
+          if (monitoring_error != NULL)
+            {
+              g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), 
reverting to "
+                         "polling: %s",
+                         cache_path,
+                         cache_type,
+                         monitoring_error->message);
+
+              can_monitor = FALSE;
+              g_error_free (monitoring_error);
+            }
         }
       else if (strcmp (cache_type, "DIR") == 0)
         {
           GFile *directory;
 
           directory = g_file_get_parent (file);
-          monitor = g_file_monitor_directory (directory,
-                                              G_FILE_MONITOR_NONE,
-                                              NULL,
-                                              &monitoring_error);
+
+          monitoring_error = NULL;
+          monitor = g_file_monitor_directory (directory, G_FILE_MONITOR_NONE, NULL, &monitoring_error);
+          if (monitoring_error != NULL)
+            {
+              g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), 
reverting to "
+                         "polling: %s",
+                         cache_path,
+                         cache_type,
+                         monitoring_error->message);
+
+              can_monitor = FALSE;
+              g_error_free (monitoring_error);
+            }
+
           g_object_unref (directory);
+        }
 
+      if (monitor != NULL)
+        {
+          g_signal_connect (G_OBJECT (monitor), "changed", G_CALLBACK 
(credentials_cache_file_monitor_changed), self);
+          self->credentials_cache_file_monitor = monitor;
         }
+
       g_object_unref (file);
     }
-
-  if (monitor == NULL)
+  else if (strcmp (cache_type, "KEYRING") == 0)
     {
+      GSource *keyring_source = NULL;
+
+      monitoring_error = NULL;
+      keyring_source = goa_kerberos_identity_manager_keyring_source_new (&monitoring_error);
       if (monitoring_error != NULL)
         {
-          g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), reverting 
to "
-                     "polling: %s",
+          g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), reverting 
to polling: %s",
                      cache_path,
                      cache_type,
-                     monitoring_error != NULL? monitoring_error->message : "");
-          g_clear_error (&monitoring_error);
+                     monitoring_error->message);
+
+          can_monitor = FALSE;
+          g_error_free (monitoring_error);
         }
-      can_monitor = FALSE;
+
+      g_source_set_callback (source, (GSourceFunc) credentials_cache_keyring_notification, self, NULL);
+      g_source_attach (source, NULL);
+
+      g_clear_pointer (&source, g_source_unref);
     }
   else
     {
-      g_signal_connect (G_OBJECT (monitor), "changed", G_CALLBACK (credentials_cache_file_monitor_changed), 
self);
-      self->credentials_cache_file_monitor = monitor;
+      can_monitor = FALSE;
     }
 
   if (!can_monitor)
     {
+      g_warning ("GoaKerberosIdentityManager: Using polling for change notification for credential cache 
type '%s'",
+                 cache_type);
+
       self->credentials_cache_polling_timeout_id = g_timeout_add_seconds (FALLBACK_POLLING_INTERVAL,
                                                                           (GSourceFunc) 
credentials_cache_polling_timeout,
                                                                           self);


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