[gnome-control-center/wip/user-identities: 5/7] user-accounts: add alarm class
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/user-identities: 5/7] user-accounts: add alarm class
- Date: Wed, 29 Feb 2012 23:40:12 +0000 (UTC)
commit 39ccafca1daf06f044d4a06b76c72184740f5aad
Author: Ray Strode <rstrode redhat com>
Date: Mon Feb 27 16:22:39 2012 -0500
user-accounts: add alarm class
When we support showing kerberos realms in the UI,
we're going to need some way to get notified when
the users credentials are no longer valid for
those realms.
This commit adds an alarm class that abstracts
timerfd so we can get proper notification when
kerberos credentials expire.
configure.ac | 53 ++++
panels/user-accounts/Makefile.am | 2 +
panels/user-accounts/um-alarm.c | 490 ++++++++++++++++++++++++++++++++++++++
panels/user-accounts/um-alarm.h | 66 +++++
4 files changed, 611 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7d33013..798cdca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -234,6 +234,59 @@ AC_SUBST(PANEL_LIBS)
PANEL_LDFLAGS="-export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload)'"
AC_SUBST(PANEL_LDFLAGS)
+dnl ==================================================
+dnl timerfd support
+dnl ==================================================
+AC_MSG_CHECKING([for timerfd support])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <sys/timerfd.h>
+#include <unistd.h>
+],[
+int
+main (void)
+{
+ struct itimerspec timer_spec = { 0 };
+ timerfd_settime (timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC),
+ TFD_TIMER_ABSTIME,
+ &timer_spec,
+ NULL);
+
+ return 0;
+}
+])],
+[have_timerfd=yes],
+[have_timerfd=no])
+AC_MSG_RESULT($have_timerfd)
+if test x"$have_timerfd" = x"yes"; then
+ AC_DEFINE(HAVE_TIMERFD, 1, [have timerfd support])
+
+ dnl libc headers tend to trail kernel support
+ dnl so compensate if necessary
+ AC_MSG_CHECKING([for timerfd cancel-on-set support])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+ #include <sys/timerfd.h>
+ #include <unistd.h>
+ ],[
+ int
+ main (void)
+ {
+ struct itimerspec timer_spec = { 0 };
+ timerfd_settime (timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC),
+ TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
+ &timer_spec,
+ NULL);
+
+ return 0;
+ }
+ ])],
+ [have_tfd_timer_cancel_on_set=yes],
+ [have_tfd_timer_cancel_on_set=no])
+ AC_MSG_RESULT($have_tfd_timer_cancel_on_set)
+ if test x"$have_tfd_timer_cancel_on_set" = x"no"; then
+ AC_DEFINE(TFD_TIMER_CANCEL_ON_SET, [(1 << 1)], [have timerfd support])
+ fi
+fi
+
dnl ==============================================
dnl libsocialweb
dnl ==============================================
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am
index ce52837..cad8b60 100644
--- a/panels/user-accounts/Makefile.am
+++ b/panels/user-accounts/Makefile.am
@@ -25,6 +25,8 @@ endif
libuser_accounts_la_SOURCES = \
um-account-type.h \
um-account-type.c \
+ um-alarm.h \
+ um-alarm.c \
um-identity-manager.h \
um-identity-manager.c \
um-identity.h \
diff --git a/panels/user-accounts/um-alarm.c b/panels/user-accounts/um-alarm.c
new file mode 100644
index 0000000..29d82e5
--- /dev/null
+++ b/panels/user-accounts/um-alarm.c
@@ -0,0 +1,490 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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 2, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Ray Strode
+ * Based on work by Colin Walters
+ */
+
+#include "config.h"
+
+#include "um-alarm.h"
+
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+
+typedef struct {
+ GSource *source;
+ GInputStream *stream;
+} Timer;
+
+typedef struct {
+ GSource *source;
+} Timeout;
+
+#define MAX_TIMEOUT_INTERVAL (10 * 1000)
+
+typedef enum {
+ UM_ALARM_TYPE_UNSCHEDULED,
+ UM_ALARM_TYPE_TIMER,
+ UM_ALARM_TYPE_TIMEOUT,
+} UmAlarmType;
+
+struct _UmAlarmPrivate
+{
+ GCancellable *cancellable;
+ GDateTime *time;
+ GDateTime *previous_wakeup_time;
+ GMainContext *context;
+ GSource *immediate_wakeup_source;
+
+ UmAlarmType type;
+ union {
+ Timer timer;
+ Timeout timeout;
+ };
+};
+
+enum {
+ FIRED,
+ REARMED,
+ NUMBER_OF_SIGNALS,
+};
+
+static void schedule_wakeups (UmAlarm *self);
+static void schedule_wakeups_with_timeout_source (UmAlarm *self);
+static guint signals[NUMBER_OF_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (UmAlarm, um_alarm, G_TYPE_OBJECT);
+
+static void
+clear_scheduled_immediate_wakeup (UmAlarm *self)
+{
+ if (self->priv->immediate_wakeup_source != NULL) {
+ g_source_destroy (self->priv->immediate_wakeup_source);
+ self->priv->immediate_wakeup_source = NULL;
+ }
+}
+
+static void
+clear_scheduled_timer_wakeups (UmAlarm *self)
+{
+#ifdef HAVE_TIMERFD
+ GError *error;
+ gboolean is_closed;
+
+ if (self->priv->timer.stream == NULL) {
+ return;
+ }
+
+ g_source_destroy (self->priv->timer.source);
+ self->priv->timer.source = NULL;
+
+ error = NULL;
+ is_closed = g_input_stream_close (self->priv->timer.stream,
+ NULL,
+ &error);
+
+ if (!is_closed) {
+ g_warning ("UmAlarm: could not close timer stream: %s",
+ error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (self->priv->timer.stream);
+ self->priv->timer.stream = NULL;
+
+#endif
+}
+
+static void
+clear_scheduled_timeout_wakeups (UmAlarm *self)
+{
+ g_source_destroy (self->priv->timeout.source);
+ self->priv->timeout.source = NULL;
+}
+
+static void
+clear_scheduled_wakeups (UmAlarm *self)
+{
+ clear_scheduled_immediate_wakeup (self);
+
+ switch (self->priv->type) {
+ case UM_ALARM_TYPE_TIMER:
+ clear_scheduled_timer_wakeups (self);
+ break;
+
+ case UM_ALARM_TYPE_TIMEOUT:
+ clear_scheduled_timeout_wakeups (self);
+ break;
+
+ default:
+ break;
+ }
+
+ if (self->priv->cancellable != NULL) {
+ g_object_unref (self->priv->cancellable);
+ self->priv->cancellable = NULL;
+ }
+
+ if (self->priv->context != NULL) {
+ g_main_context_unref (self->priv->context);
+ self->priv->context = NULL;
+ }
+
+ if (self->priv->previous_wakeup_time != NULL) {
+ g_date_time_unref (self->priv->previous_wakeup_time);
+ self->priv->previous_wakeup_time = NULL;
+ }
+
+ self->priv->type = UM_ALARM_TYPE_UNSCHEDULED;
+}
+
+static void
+um_alarm_finalize (GObject *object)
+{
+ UmAlarm *self = UM_ALARM (object);
+
+ if (self->priv->cancellable != NULL &&
+ !g_cancellable_is_cancelled (self->priv->cancellable)) {
+ g_cancellable_cancel (self->priv->cancellable);
+ }
+
+ clear_scheduled_wakeups (self);
+
+ if (self->priv->time != NULL) {
+ g_date_time_unref (self->priv->time);
+ }
+
+ if (self->priv->previous_wakeup_time != NULL) {
+ g_date_time_unref (self->priv->previous_wakeup_time);
+ }
+
+ G_OBJECT_CLASS (um_alarm_parent_class)->finalize (object);
+}
+
+static void
+um_alarm_class_init (UmAlarmClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = um_alarm_finalize;
+
+ g_type_class_add_private (klass, sizeof (UmAlarmPrivate));
+
+ signals[FIRED] = g_signal_new ("fired",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals[REARMED] = g_signal_new ("rearmed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+um_alarm_init (UmAlarm *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ UM_TYPE_ALARM,
+ UmAlarmPrivate);
+ self->priv->type = UM_ALARM_TYPE_UNSCHEDULED;
+}
+
+static void
+on_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+{
+ UmAlarm *self = UM_ALARM (user_data);
+
+ clear_scheduled_wakeups (self);
+}
+
+static void
+fire_alarm (UmAlarm *self)
+{
+ g_signal_emit (G_OBJECT (self), signals[FIRED], 0);
+}
+
+static void
+rearm_alarm (UmAlarm *self)
+{
+ g_signal_emit (G_OBJECT (self), signals[REARMED], 0);
+}
+
+static void
+fire_or_rearm_alarm (UmAlarm *self)
+{
+ GTimeSpan time_until_fire;
+ GTimeSpan previous_time_until_fire;
+ GDateTime *now;
+
+ now = g_date_time_new_now_local ();
+ time_until_fire = g_date_time_difference (self->priv->time, now);
+
+ if (self->priv->previous_wakeup_time == NULL) {
+ self->priv->previous_wakeup_time = now;
+
+ /* If, according to the time, we're past when we should have fired,
+ * then fire the alarm.
+ */
+ if (time_until_fire <= 0) {
+ fire_alarm (self);
+ }
+ } else {
+ previous_time_until_fire = g_date_time_difference (self->priv->time,
+ self->priv->previous_wakeup_time);
+
+ g_date_time_unref (self->priv->previous_wakeup_time);
+ self->priv->previous_wakeup_time = now;
+
+ /* If, according to the time, we're past when we should have fired,
+ * and this is the first wakeup where that's been true then fire
+ * the alarm. The first check makes sure we don't fire prematurely,
+ * and the second check makes sure we don't fire more than once
+ */
+ if (time_until_fire <= 0 && previous_time_until_fire > 0) {
+ fire_alarm (self);
+
+ /* If, according to the time, we're before when we should fire,
+ * and we previously fired the alarm, then we've jumped back in
+ * time and need to rearm the alarm.
+ */
+ } else if (time_until_fire > 0 && previous_time_until_fire <= 0) {
+ rearm_alarm (self);
+ }
+ }
+}
+
+static gboolean
+on_immediate_wakeup_source_ready (gpointer user_data)
+{
+ UmAlarm *self = UM_ALARM (user_data);
+
+ fire_or_rearm_alarm (self);
+
+ return FALSE;
+}
+
+#ifdef HAVE_TIMERFD
+static gboolean
+on_timer_source_ready (GObject *stream,
+ gpointer user_data)
+{
+ UmAlarm *self = UM_ALARM (user_data);
+ gint64 number_of_fires;
+ gssize bytes_read;
+
+ bytes_read = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
+ &number_of_fires,
+ sizeof (gint64),
+ NULL,
+ NULL);
+
+ if (bytes_read == sizeof (gint64)) {
+ if (number_of_fires < 0 || number_of_fires > 1) {
+ g_warning ("UmAlarm: expected timerfd to report firing once,"
+ "but it reported firing %ld times\n",
+ (long) number_of_fires);
+ }
+ }
+
+ fire_or_rearm_alarm (self);
+ return TRUE;
+}
+#endif
+
+static gboolean
+schedule_wakeups_with_timerfd (UmAlarm *self)
+{
+#ifdef HAVE_TIMERFD
+ struct itimerspec timer_spec;
+ int fd;
+ int result;
+
+ g_debug ("UmAlarm: trying to use kernel timer");
+
+ fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC | TFD_NONBLOCK);
+
+ if (fd < 0) {
+ g_debug ("UmAlarm: could not create timer fd: %m");
+ return FALSE;
+ }
+
+ memset (&timer_spec, 0, sizeof (timer_spec));
+ timer_spec.it_value.tv_sec = g_date_time_to_unix (self->priv->time) + 1;
+
+ result = timerfd_settime (fd,
+ TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
+ &timer_spec,
+ NULL);
+
+ if (result < 0) {
+ g_debug ("UmAlarm: could not set timer: %m");
+ return FALSE;
+ }
+
+ self->priv->type = UM_ALARM_TYPE_TIMER;
+ self->priv->timer.stream = g_unix_input_stream_new (fd, TRUE);
+
+ self->priv->timer.source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (self->priv->timer.stream),
+ self->priv->cancellable);
+ g_source_set_callback (self->priv->timer.source,
+ (GSourceFunc)
+ on_timer_source_ready,
+ self,
+ NULL);
+ g_source_attach (self->priv->timer.source,
+ self->priv->context);
+
+ return TRUE;
+
+#endif /* HAVE_TIMERFD */
+
+ return FALSE;
+}
+
+static gboolean
+on_timeout_source_ready (gpointer user_data)
+{
+ UmAlarm *self = UM_ALARM (user_data);
+
+ fire_or_rearm_alarm (self);
+
+ schedule_wakeups_with_timeout_source (self);
+
+ return FALSE;
+}
+
+static void
+schedule_wakeups_with_timeout_source (UmAlarm *self)
+{
+ GDateTime *now;
+ GTimeSpan time_span;
+ guint interval;
+ self->priv->type = UM_ALARM_TYPE_TIMEOUT;
+
+ now = g_date_time_new_now_local ();
+ time_span = g_date_time_difference (self->priv->time, now);
+ g_date_time_unref (now);
+
+ time_span = CLAMP (time_span, 1000 * G_TIME_SPAN_MILLISECOND, G_MAXUINT * G_TIME_SPAN_MILLISECOND);
+ interval = time_span / G_TIME_SPAN_MILLISECOND;
+
+ /* We poll every 10 seconds or so because we want to catch time skew
+ */
+ interval = MIN (interval, MAX_TIMEOUT_INTERVAL);
+
+ self->priv->timeout.source = g_timeout_source_new (interval);
+ g_source_set_callback (self->priv->timeout.source,
+ (GSourceFunc)
+ on_timeout_source_ready,
+ self,
+ NULL);
+
+ g_source_attach (self->priv->timeout.source,
+ self->priv->context);
+}
+
+static void
+schedule_wakeups (UmAlarm *self)
+{
+ gboolean wakeup_scheduled;
+
+ wakeup_scheduled = schedule_wakeups_with_timerfd (self);
+
+ if (!wakeup_scheduled) {
+ g_debug ("UmAlarm: falling back to polling timeout\n");
+ schedule_wakeups_with_timeout_source (self);
+ }
+}
+
+static void
+schedule_immediate_wakeup (UmAlarm *self)
+{
+ self->priv->immediate_wakeup_source = g_idle_source_new ();
+
+ g_source_set_callback (self->priv->immediate_wakeup_source,
+ (GSourceFunc)
+ on_immediate_wakeup_source_ready,
+ self,
+ NULL);
+
+ g_source_attach (self->priv->immediate_wakeup_source,
+ self->priv->context);
+}
+
+void
+um_alarm_set (UmAlarm *self,
+ GDateTime *time,
+ GCancellable *cancellable)
+{
+ if (self->priv->cancellable != NULL) {
+ if (!g_cancellable_is_cancelled (self->priv->cancellable)) {
+ g_cancellable_cancel (cancellable);
+ }
+
+ if (self->priv->cancellable != NULL) {
+ g_object_unref (self->priv->cancellable);
+ self->priv->cancellable = NULL;
+ }
+ }
+
+ if (cancellable == NULL) {
+ self->priv->cancellable = g_cancellable_new ();
+ } else {
+ self->priv->cancellable = g_object_ref (cancellable);
+ }
+
+ g_cancellable_connect (self->priv->cancellable,
+ G_CALLBACK (on_cancelled),
+ self,
+ NULL);
+ self->priv->time = g_date_time_ref (time);
+ self->priv->context = g_main_context_ref (g_main_context_default ());
+
+ schedule_wakeups (self);
+
+ /* Wake up right away, in case it's already expired leaving the gate */
+ schedule_immediate_wakeup (self);
+}
+
+UmAlarm *
+um_alarm_new (void)
+{
+ UmAlarm *self;
+
+ self = UM_ALARM (g_object_new (UM_TYPE_ALARM, NULL));
+
+ return UM_ALARM (self);
+}
diff --git a/panels/user-accounts/um-alarm.h b/panels/user-accounts/um-alarm.h
new file mode 100644
index 0000000..6eeb8d7
--- /dev/null
+++ b/panels/user-accounts/um-alarm.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Authors: Ray Strode
+ */
+
+#ifndef __UM_ALARM_H__
+#define __UM_ALARM_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define UM_TYPE_ALARM (um_alarm_get_type ())
+#define UM_ALARM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_ALARM, UmAlarm))
+#define UM_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_ALARM, UmAlarmClass))
+#define UM_IS_ALARM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_ALARM))
+#define UM_IS_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UM_TYPE_ALARM))
+#define UM_ALARM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), UM_TYPE_ALARM, UmAlarmClass))
+
+typedef struct _UmAlarm UmAlarm;
+typedef struct _UmAlarmClass UmAlarmClass;
+typedef struct _UmAlarmPrivate UmAlarmPrivate;
+
+struct _UmAlarm
+{
+ GObject parent;
+
+ UmAlarmPrivate *priv;
+};
+
+struct _UmAlarmClass
+{
+ GObjectClass parent_class;
+
+ void (* fired) (UmAlarm *alarm);
+ void (* rearmed) (UmAlarm *alarm);
+};
+
+GType um_alarm_get_type (void);
+
+UmAlarm *um_alarm_new (void);
+void um_alarm_set (UmAlarm *alarm,
+ GDateTime *time,
+ GCancellable *cancellable);
+G_END_DECLS
+
+#endif /* __UM_ALARM_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]