d873dd1
--- /dev/null	2007-03-12 21:48:12.974260066 -0400
d873dd1
+++ gnome-screensaver-2.18.0/src/securitytokenmonitor.h	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -0,0 +1,84 @@
d873dd1
+/* securitytokenmonitor.h - monitor for security token insertion and
d873dd1
+ *                          removal events
d873dd1
+ *
d873dd1
+ * Copyright (C) 2006 Ray Strode
d873dd1
+ *
d873dd1
+ * This program is free software; you can redistribute it and/or modify
d873dd1
+ * it under the terms of the GNU General Public License as published by
d873dd1
+ * the Free Software Foundation; either version 2, or (at your option)
d873dd1
+ * any later version.
d873dd1
+ *
d873dd1
+ * This program is distributed in the hope that it will be useful,
d873dd1
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
d873dd1
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d873dd1
+ * GNU General Public License for more details.
d873dd1
+ *
d873dd1
+ * You should have received a copy of the GNU General Public License
d873dd1
+ * along with this program; if not, write to the Free Software
d873dd1
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
d873dd1
+ * 02111-1307, USA.  
d873dd1
+ */
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_H
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR_H
d873dd1
+
d873dd1
+#define SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
d873dd1
+#include "securitytoken.h"
d873dd1
+
d873dd1
+#include <glib.h>
d873dd1
+#include <glib-object.h>
d873dd1
+
d873dd1
+G_BEGIN_DECLS
d873dd1
+#define SC_TYPE_SECURITY_TOKEN_MONITOR            (sc_security_token_monitor_get_type ())
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_SECURITY_TOKEN_MONITOR, ScSecurityTokenMonitor))
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_SECURITY_TOKEN_MONITOR, ScSecurityTokenMonitorClass))
d873dd1
+#define SC_IS_SECURITY_TOKEN_MONITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SECURITY_TOKEN_MONITOR))
d873dd1
+#define SC_IS_SECURITY_TOKEN_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SECURITY_TOKEN_MONITOR))
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), SC_TYPE_SECURITY_TOKEN_MONITOR, ScSecurityTokenMonitorClass))
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR_ERROR           (sc_security_token_monitor_error_quark ())
d873dd1
+typedef struct _ScSecurityTokenMonitor ScSecurityTokenMonitor;
d873dd1
+typedef struct _ScSecurityTokenMonitorClass ScSecurityTokenMonitorClass;
d873dd1
+typedef struct _ScSecurityTokenMonitorPrivate ScSecurityTokenMonitorPrivate;
d873dd1
+typedef enum _ScSecurityTokenMonitorError ScSecurityTokenMonitorError;
d873dd1
+
d873dd1
+struct _ScSecurityTokenMonitor {
d873dd1
+    GObject parent;
d873dd1
+
d873dd1
+    /*< private > */
d873dd1
+    ScSecurityTokenMonitorPrivate *priv;
d873dd1
+};
d873dd1
+
d873dd1
+struct _ScSecurityTokenMonitorClass {
d873dd1
+    GObjectClass parent_class;
d873dd1
+
d873dd1
+    /* Signals */
d873dd1
+    void (*security_token_inserted) (ScSecurityTokenMonitor *monitor,
d873dd1
+				     ScSecurityToken *token);
d873dd1
+    void (*security_token_removed) (ScSecurityTokenMonitor *monitor,
d873dd1
+				    ScSecurityToken *token);
d873dd1
+    void (*error) (ScSecurityTokenMonitor *monitor, 
d873dd1
+		   GError                 *error);
d873dd1
+};
d873dd1
+
d873dd1
+enum _ScSecurityTokenMonitorError {
d873dd1
+    SC_SECURITY_TOKEN_MONITOR_ERROR_GENERIC = 0,
d873dd1
+    SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
d873dd1
+    SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
d873dd1
+    SC_SECURITY_TOKEN_MONITOR_ERROR_WATCHING_FOR_EVENTS,
d873dd1
+    SC_SECURITY_TOKEN_MONITOR_ERROR_REPORTING_EVENTS
d873dd1
+};
d873dd1
+
d873dd1
+GType sc_security_token_monitor_get_type (void) G_GNUC_CONST;
d873dd1
+GQuark sc_security_token_monitor_error_quark (void) G_GNUC_CONST;
d873dd1
+
d873dd1
+ScSecurityTokenMonitor *sc_security_token_monitor_new (const gchar *module);
d873dd1
+
d873dd1
+gboolean sc_security_token_monitor_start (ScSecurityTokenMonitor  *monitor, 
d873dd1
+				 	  GError                 **error);
d873dd1
+
d873dd1
+void sc_security_token_monitor_stop (ScSecurityTokenMonitor *monitor);
d873dd1
+
d873dd1
+gchar *sc_security_token_monitor_get_module_path (ScSecurityTokenMonitor *monitor);
d873dd1
+gboolean sc_security_token_monitor_login_token_is_inserted (ScSecurityTokenMonitor *monitor);
d873dd1
+
d873dd1
+G_END_DECLS
d873dd1
+#endif				/* SC_SECURITY_TOKEN_MONITOR_H */
d873dd1
--- /dev/null	2007-03-12 21:48:12.974260066 -0400
d873dd1
+++ gnome-screensaver-2.18.0/src/securitytoken.h	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -0,0 +1,94 @@
d873dd1
+/* securitytoken.h - api for reading and writing data to a security token 
d873dd1
+ *
d873dd1
+ * Copyright (C) 2006 Ray Strode
d873dd1
+ *
d873dd1
+ * This program is free software; you can redistribute it and/or modify
d873dd1
+ * it under the terms of the GNU General Public License as published by
d873dd1
+ * the Free Software Foundation; either version 2, or (at your option)
d873dd1
+ * any later version.
d873dd1
+ *
d873dd1
+ * This program is distributed in the hope that it will be useful,
d873dd1
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
d873dd1
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d873dd1
+ * GNU General Public License for more details.
d873dd1
+ *
d873dd1
+ * You should have received a copy of the GNU General Public License
d873dd1
+ * along with this program; if not, write to the Free Software
d873dd1
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
d873dd1
+ * 02111-1307, USA.  
d873dd1
+ */
d873dd1
+#ifndef SC_SECURITY_TOKEN_H
d873dd1
+#define SC_SECURITY_TOKEN_H
d873dd1
+
d873dd1
+#include <glib.h>
d873dd1
+#include <glib-object.h>
d873dd1
+
d873dd1
+#include <secmod.h>
d873dd1
+
d873dd1
+G_BEGIN_DECLS
d873dd1
+#define SC_TYPE_SECURITY_TOKEN            (sc_security_token_get_type ())
d873dd1
+#define SC_SECURITY_TOKEN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_SECURITY_TOKEN, ScSecurityToken))
d873dd1
+#define SC_SECURITY_TOKEN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_SECURITY_TOKEN, ScSecurityTokenClass))
d873dd1
+#define SC_IS_SECURITY_TOKEN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SECURITY_TOKEN))
d873dd1
+#define SC_IS_SECURITY_TOKEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SECURITY_TOKEN))
d873dd1
+#define SC_SECURITY_TOKEN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), SC_TYPE_SECURITY_TOKEN, ScSecurityTokenClass))
d873dd1
+#define SC_SECURITY_TOKEN_ERROR           (sc_security_token_error_quark ())
d873dd1
+typedef struct _ScSecurityTokenClass ScSecurityTokenClass;
d873dd1
+typedef struct _ScSecurityToken ScSecurityToken;
d873dd1
+typedef struct _ScSecurityTokenPrivate ScSecurityTokenPrivate;
d873dd1
+typedef enum _ScSecurityTokenError ScSecurityTokenError;
d873dd1
+typedef enum _ScSecurityTokenState ScSecurityTokenState;
d873dd1
+
d873dd1
+typedef struct _ScSecurityTokenRequest ScSecurityTokenRequest;
d873dd1
+
d873dd1
+struct _ScSecurityToken {
d873dd1
+    GObject parent;
d873dd1
+
d873dd1
+    /*< private > */
d873dd1
+    ScSecurityTokenPrivate *priv;
d873dd1
+};
d873dd1
+
d873dd1
+struct _ScSecurityTokenClass {
d873dd1
+    GObjectClass parent_class;
d873dd1
+
d873dd1
+    void (* inserted) (ScSecurityToken *token);
d873dd1
+    void (* removed) (ScSecurityToken *token);
d873dd1
+};
d873dd1
+
d873dd1
+enum _ScSecurityTokenError {
d873dd1
+    SC_SECURITY_TOKEN_ERROR_GENERIC = 0,
d873dd1
+};
d873dd1
+
d873dd1
+enum _ScSecurityTokenState {
d873dd1
+    SC_SECURITY_TOKEN_STATE_INSERTED = 0,
d873dd1
+    SC_SECURITY_TOKEN_STATE_REMOVED,
d873dd1
+};
d873dd1
+
d873dd1
+GType sc_security_token_get_type (void) G_GNUC_CONST;
d873dd1
+GQuark sc_security_token_error_quark (void) G_GNUC_CONST;
d873dd1
+
d873dd1
+CK_SLOT_ID sc_security_token_get_slot_id (ScSecurityToken *token);
d873dd1
+gint sc_security_token_get_slot_series (ScSecurityToken *token);
d873dd1
+ScSecurityTokenState sc_security_token_get_state (ScSecurityToken *token);
d873dd1
+
d873dd1
+gchar *sc_security_token_get_name (ScSecurityToken *token);
d873dd1
+gboolean sc_security_token_is_login_token (ScSecurityToken *token);
d873dd1
+
d873dd1
+gboolean sc_security_token_unlock (ScSecurityToken *token,
d873dd1
+				   const gchar     *password);
d873dd1
+
d873dd1
+/* don't under any circumstances call these functions */
d873dd1
+#ifdef SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
d873dd1
+
d873dd1
+ScSecurityToken *_sc_security_token_new (SECMODModule *module,
d873dd1
+					 CK_SLOT_ID slot_id, 
d873dd1
+					 gint slot_series);
d873dd1
+ScSecurityToken *_sc_security_token_new_from_name (SECMODModule *module,
d873dd1
+						   const gchar *name);
d873dd1
+
d873dd1
+void _sc_security_token_set_state (ScSecurityToken      *token,
d873dd1
+				   ScSecurityTokenState  state);
d873dd1
+#endif 
d873dd1
+
d873dd1
+G_END_DECLS
d873dd1
+#endif				/* SC_SECURITY_TOKEN_H */
d873dd1
--- gnome-screensaver-2.18.0/src/gs-monitor.c.securitytoken	2007-02-22 23:39:09.000000000 -0500
d873dd1
+++ gnome-screensaver-2.18.0/src/gs-monitor.c	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -41,10 +41,15 @@
d873dd1
 #include "gs-prefs.h"
d873dd1
 #include "gs-debug.h"
d873dd1
 
d873dd1
+#include "securitytokenmonitor.h"
d873dd1
+
d873dd1
 static void     gs_monitor_class_init (GSMonitorClass *klass);
d873dd1
 static void     gs_monitor_init       (GSMonitor      *monitor);
d873dd1
 static void     gs_monitor_finalize   (GObject        *object);
d873dd1
 
d873dd1
+static void     gs_monitor_simulate_user_activity (GSMonitor *monitor);
d873dd1
+static void     gs_monitor_lock_screen (GSMonitor *monitor);
d873dd1
+
d873dd1
 #define GS_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_MONITOR, GSMonitorPrivate))
d873dd1
 
d873dd1
 struct GSMonitorPrivate
d873dd1
@@ -56,6 +61,8 @@
d873dd1
         GSFade         *fade;
d873dd1
         GSGrab         *grab;
d873dd1
 
d873dd1
+	ScSecurityTokenMonitor *security_token_monitor;
d873dd1
+
d873dd1
         guint           release_grab_id;
d873dd1
 };
d873dd1
 
d873dd1
@@ -100,6 +107,36 @@
d873dd1
         gs_listener_emit_auth_request_end (monitor->priv->listener);
d873dd1
 }
d873dd1
 
d873dd1
+static void
d873dd1
+security_token_inserted_cb (ScSecurityTokenMonitor *token_monitor,
d873dd1
+			    ScSecurityToken        *token,
d873dd1
+			    GSMonitor              *monitor)
d873dd1
+{
d873dd1
+	gs_monitor_simulate_user_activity (monitor);
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+gs_monitor_should_lock_on_login_security_token_removal (void)
d873dd1
+{
d873dd1
+    /* FIXME: lame hack
d873dd1
+     */
d873dd1
+    return system ("pkcs11_setup rm_action | grep -q lock") == 0;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+security_token_removed_cb (ScSecurityTokenMonitor *token_monitor,
d873dd1
+			   ScSecurityToken        *token,
d873dd1
+			   GSMonitor              *monitor)
d873dd1
+{
d873dd1
+    if (gs_monitor_should_lock_on_login_security_token_removal () &&
d873dd1
+	sc_security_token_is_login_token (token))
d873dd1
+	    gs_monitor_lock_screen (monitor);
d873dd1
+
d873dd1
+    /* If we're already locked and the lock dialog is up, kill it.
d873dd1
+     */
d873dd1
+    gs_manager_cancel_unlock_request (monitor->priv->manager);
d873dd1
+}
d873dd1
+
d873dd1
 static gboolean
d873dd1
 watcher_idle_cb (GSWatcher *watcher,
d873dd1
                  gboolean   is_idle,
d873dd1
@@ -210,37 +247,6 @@
d873dd1
 }
d873dd1
 
d873dd1
 static void
d873dd1
-gs_monitor_lock_screen (GSMonitor *monitor)
d873dd1
-{
d873dd1
-        gboolean res;
d873dd1
-        gboolean locked;
d873dd1
-
d873dd1
-        /* set lock flag before trying to activate screensaver
d873dd1
-           in case something tries to react to the ActiveChanged signal */
d873dd1
-
d873dd1
-        gs_manager_get_lock_active (monitor->priv->manager, &locked);
d873dd1
-        gs_manager_set_lock_active (monitor->priv->manager, TRUE);
d873dd1
-        res = gs_listener_set_active (monitor->priv->listener, TRUE);
d873dd1
-        if (! res) {
d873dd1
-                /* If we've failed then restore lock status */
d873dd1
-                gs_manager_set_lock_active (monitor->priv->manager, locked);
d873dd1
-                gs_debug ("Unable to lock the screen");
d873dd1
-        }
d873dd1
-}
d873dd1
-
d873dd1
-static void
d873dd1
-gs_monitor_simulate_user_activity (GSMonitor *monitor)
d873dd1
-{
d873dd1
-        /* in case the screen isn't blanked reset the
d873dd1
-           idle watcher */
d873dd1
-        gs_watcher_reset (monitor->priv->watcher);
d873dd1
-
d873dd1
-        /* request that the manager unlock -
d873dd1
-           will pop up a dialog if necessary */
d873dd1
-        gs_manager_request_unlock (monitor->priv->manager);
d873dd1
-}
d873dd1
-
d873dd1
-static void
d873dd1
 listener_lock_cb (GSListener *listener,
d873dd1
                   GSMonitor  *monitor)
d873dd1
 {
d873dd1
@@ -424,6 +430,27 @@
d873dd1
 }
d873dd1
 
d873dd1
 static void
d873dd1
+disconnect_security_token_monitor_signals (GSMonitor *monitor)
d873dd1
+{
d873dd1
+        g_signal_handlers_disconnect_by_func (monitor->priv->security_token_monitor,
d873dd1
+					      security_token_removed_cb, monitor);
d873dd1
+
d873dd1
+        g_signal_handlers_disconnect_by_func (monitor->priv->security_token_monitor,
d873dd1
+					      security_token_inserted_cb, monitor);
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+connect_security_token_monitor_signals (GSMonitor *monitor)
d873dd1
+{
d873dd1
+	g_signal_connect (monitor->priv->security_token_monitor, 
d873dd1
+			  "security-token-removed",
d873dd1
+			  G_CALLBACK (security_token_removed_cb), monitor);
d873dd1
+	g_signal_connect (monitor->priv->security_token_monitor, 
d873dd1
+			  "security-token-inserted",
d873dd1
+			  G_CALLBACK (security_token_inserted_cb), monitor);
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
 disconnect_prefs_signals (GSMonitor *monitor)
d873dd1
 {
d873dd1
         g_signal_handlers_disconnect_by_func (monitor->priv->prefs, _gs_monitor_update_from_prefs, monitor);
d873dd1
@@ -457,6 +484,26 @@
d873dd1
         monitor->priv->manager = gs_manager_new ();
d873dd1
         connect_manager_signals (monitor);
d873dd1
 
d873dd1
+	/* PKCS11_LOGIN_TOKEN_NAME is set if the user logged in with a
d873dd1
+	 * security token.
d873dd1
+	 */
d873dd1
+	if (g_getenv ("PKCS11_LOGIN_TOKEN_NAME") != NULL) {
d873dd1
+		monitor->priv->security_token_monitor = sc_security_token_monitor_new (NULL);
d873dd1
+		sc_security_token_monitor_start (monitor->priv->security_token_monitor,
d873dd1
+						 NULL);
d873dd1
+		connect_security_token_monitor_signals (monitor);
d873dd1
+
d873dd1
+		/* if the user logged in with a security token but it's
d873dd1
+		 * not currently inserted, then they must have yanked it
d873dd1
+		 * before we started.  lock the screen immediately
d873dd1
+		 */
d873dd1
+		if (gs_monitor_should_lock_on_login_security_token_removal () &&
d873dd1
+		    !sc_security_token_monitor_login_token_is_inserted (monitor->priv->security_token_monitor))
d873dd1
+			gs_monitor_lock_screen (monitor);
d873dd1
+	} else {
d873dd1
+		monitor->priv->security_token_monitor = NULL;
d873dd1
+	}
d873dd1
+
d873dd1
         _gs_monitor_update_from_prefs (monitor, monitor->priv->prefs);
d873dd1
 }
d873dd1
 
d873dd1
@@ -472,6 +519,12 @@
d873dd1
 
d873dd1
         g_return_if_fail (monitor->priv != NULL);
d873dd1
 
d873dd1
+	if (monitor->priv->security_token_monitor != NULL) {
d873dd1
+		sc_security_token_monitor_stop (monitor->priv->security_token_monitor);
d873dd1
+		disconnect_security_token_monitor_signals (monitor);
d873dd1
+		g_object_unref (monitor->priv->security_token_monitor);
d873dd1
+	}
d873dd1
+
d873dd1
         disconnect_watcher_signals (monitor);
d873dd1
         disconnect_listener_signals (monitor);
d873dd1
         disconnect_manager_signals (monitor);
d873dd1
@@ -509,3 +562,34 @@
d873dd1
 
d873dd1
         return TRUE;
d873dd1
 }
d873dd1
+
d873dd1
+static void     
d873dd1
+gs_monitor_simulate_user_activity (GSMonitor *monitor)
d873dd1
+{
d873dd1
+        /* in case the screen isn't blanked reset the
d873dd1
+           idle watcher */
d873dd1
+        gs_watcher_reset (monitor->priv->watcher);
d873dd1
+
d873dd1
+        /* request that the manager unlock -
d873dd1
+           will pop up a dialog if necessary */
d873dd1
+        gs_manager_request_unlock (monitor->priv->manager);
d873dd1
+}
d873dd1
+
d873dd1
+static void     
d873dd1
+gs_monitor_lock_screen (GSMonitor *monitor)
d873dd1
+{
d873dd1
+        gboolean res;
d873dd1
+        gboolean locked;
d873dd1
+
d873dd1
+        /* set lock flag before trying to activate screensaver
d873dd1
+           in case something tries to react to the ActiveChanged signal */
d873dd1
+
d873dd1
+        gs_manager_get_lock_active (monitor->priv->manager, &locked);
d873dd1
+        gs_manager_set_lock_active (monitor->priv->manager, TRUE);
d873dd1
+        res = gs_listener_set_active (monitor->priv->listener, TRUE);
d873dd1
+        if (! res) {
d873dd1
+                /* If we've failed then restore lock status */
d873dd1
+                gs_manager_set_lock_active (monitor->priv->manager, locked);
d873dd1
+                gs_debug ("Unable to lock the screen");
d873dd1
+        }
d873dd1
+}
d873dd1
--- gnome-screensaver-2.18.0/src/gs-auth-pam.c.securitytoken	2007-02-22 23:39:09.000000000 -0500
d873dd1
+++ gnome-screensaver-2.18.0/src/gs-auth-pam.c	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -354,6 +354,13 @@
d873dd1
                                    status2,
d873dd1
                                    (status2 == PAM_SUCCESS ? "Success" : "Failure"));
d873dd1
                 }
d873dd1
+
d873dd1
+		/* iterate the glib event loop inbetween processing pam
d873dd1
+		 * messages so that the user interface can be updated
d873dd1
+		 * to reflect changes that are a result of the pam
d873dd1
+		 * messages
d873dd1
+		 */
d873dd1
+		while (g_main_context_iteration (NULL, FALSE));
d873dd1
         }
d873dd1
 
d873dd1
         if (message_handled_condition != NULL) {
d873dd1
--- /dev/null	2007-03-12 21:48:12.974260066 -0400
d873dd1
+++ gnome-screensaver-2.18.0/src/securitytokenmonitor.c	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -0,0 +1,1743 @@
d873dd1
+/* securitytokenmonitor.c - monitor for security token insertion and
d873dd1
+ *                          removal events
d873dd1
+ * 
d873dd1
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
d873dd1
+ * 
d873dd1
+ * This program is free software; you can redistribute it and/or modify
d873dd1
+ * it under the terms of the GNU General Public License as published by
d873dd1
+ * the Free Software Foundation; either version 2, or (at your option)
d873dd1
+ * any later version.
d873dd1
+ * 
d873dd1
+ * This program is distributed in the hope that it will be useful,
d873dd1
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
d873dd1
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d873dd1
+ * GNU General Public License for more details.
d873dd1
+ * 
d873dd1
+ * You should have received a copy of the GNU General Public License
d873dd1
+ * along with this program; if not, write to the Free Software
d873dd1
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
d873dd1
+ * 02111-1307, USA.  
d873dd1
+ *
d873dd1
+ * TODO:     - doing this per project is a bad idea i think.
d873dd1
+ *             We should probably make this a system service 
d873dd1
+ *             and use dbus.
d873dd1
+ */
d873dd1
+#define _GNU_SOURCE
d873dd1
+#include "securitytokenmonitor.h"
d873dd1
+
d873dd1
+#define SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
d873dd1
+#include "securitytoken.h"
d873dd1
+
d873dd1
+#include <dirent.h>
d873dd1
+#include <errno.h>
d873dd1
+#include <fcntl.h>
d873dd1
+#include <limits.h>
d873dd1
+#include <poll.h>
d873dd1
+#include <signal.h>
d873dd1
+#include <stdlib.h>
d873dd1
+#include <string.h>
d873dd1
+#include <sys/resource.h>
d873dd1
+#include <sys/time.h>
d873dd1
+#include <sys/wait.h>
d873dd1
+#include <unistd.h>
d873dd1
+
d873dd1
+#include <glib.h>
d873dd1
+#include <glib/gi18n.h>
d873dd1
+
d873dd1
+#include <prerror.h>
d873dd1
+#include <nss.h>
d873dd1
+#include <pk11func.h>
d873dd1
+#include <secmod.h>
d873dd1
+#include <secerr.h>
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so"
d873dd1
+#endif
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_NSS_DB
d873dd1
+#define SC_SECURITY_TOKEN_MONITOR_NSS_DB SYSCONFDIR"/pki/nssdb"
d873dd1
+#endif 
d873dd1
+
d873dd1
+#ifndef SC_MAX_OPEN_FILE_DESCRIPTORS
d873dd1
+#define SC_MAX_OPEN_FILE_DESCRIPTORS 1024
d873dd1
+#endif
d873dd1
+
d873dd1
+#ifndef SC_OPEN_FILE_DESCRIPTORS_DIR
d873dd1
+#define SC_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd"
d873dd1
+#endif
d873dd1
+
d873dd1
+#ifndef sc_debug
d873dd1
+#if defined (SC_SECURITY_TOKEN_MONITOR_ENABLE_TEST)
d873dd1
+#define sc_debug(fmt, args...) g_printerr("[%u] " fmt " \n", getpid(), ##args)
d873dd1
+#else
d873dd1
+#define sc_debug(fmt, args...) 
d873dd1
+#endif
d873dd1
+#endif
d873dd1
+
d873dd1
+typedef enum _ScSecurityTokenMonitorState ScSecurityTokenMonitorState;
d873dd1
+typedef struct _ScSecurityTokenMonitorWorker ScSecurityTokenMonitorWorker;
d873dd1
+
d873dd1
+enum _ScSecurityTokenMonitorState {
d873dd1
+	SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED = 0,
d873dd1
+	SC_SECURITY_TOKEN_MONITOR_STATE_STARTING,
d873dd1
+	SC_SECURITY_TOKEN_MONITOR_STATE_STARTED,
d873dd1
+	SC_SECURITY_TOKEN_MONITOR_STATE_STOPPING,
d873dd1
+};
d873dd1
+
d873dd1
+struct _ScSecurityTokenMonitorPrivate {
d873dd1
+	ScSecurityTokenMonitorState state;
d873dd1
+	SECMODModule *module;
d873dd1
+	gchar        *module_path;
d873dd1
+
d873dd1
+	GSource *security_token_event_source;
d873dd1
+	GPid security_token_event_watcher_pid;
d873dd1
+	GHashTable *security_tokens;
d873dd1
+
d873dd1
+	guint poll_timeout_id;
d873dd1
+
d873dd1
+	guint32 is_unstoppable : 1;
d873dd1
+	guint32 nss_is_loaded : 1;
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
d873dd1
+	GArray *fds_to_close_on_fork;
d873dd1
+#endif 
d873dd1
+};
d873dd1
+
d873dd1
+struct _ScSecurityTokenMonitorWorker {
d873dd1
+	SECMODModule *module;
d873dd1
+	GHashTable *security_tokens;
d873dd1
+	gint write_fd;
d873dd1
+
d873dd1
+	guint32 nss_is_loaded : 1;
d873dd1
+};
d873dd1
+
d873dd1
+static void sc_security_token_monitor_finalize (GObject *object);
d873dd1
+static void sc_security_token_monitor_class_install_signals (ScSecurityTokenMonitorClass *service_class);
d873dd1
+static void sc_security_token_monitor_class_install_properties (ScSecurityTokenMonitorClass *service_class);
d873dd1
+static void sc_security_token_monitor_set_property (GObject       *object,
d873dd1
+						    guint          prop_id,
d873dd1
+						    const GValue  *value,
d873dd1
+						    GParamSpec    *pspec);
d873dd1
+static void sc_security_token_monitor_get_property (GObject    *object,
d873dd1
+						    guint       prop_id,
d873dd1
+						    GValue     *value,
d873dd1
+						    GParamSpec *pspec);
d873dd1
+static void sc_security_token_monitor_set_module_path (ScSecurityTokenMonitor *monitor,
d873dd1
+						       const gchar            *module_path);
d873dd1
+static void sc_security_token_monitor_token_removed_handler (ScSecurityTokenMonitor *monitor,
d873dd1
+							     ScSecurityToken        *token);
d873dd1
+static void sc_security_token_monitor_token_inserted_handler (ScSecurityTokenMonitor *monitor_class,
d873dd1
+							      ScSecurityToken        *token);
d873dd1
+static gboolean sc_security_token_monitor_stop_now (ScSecurityTokenMonitor *monitor);
d873dd1
+static void sc_security_token_monitor_queue_stop (ScSecurityTokenMonitor *monitor);
d873dd1
+
d873dd1
+static gboolean sc_security_token_monitor_create_worker (ScSecurityTokenMonitor *monitor,
d873dd1
+							 gint *worker_fd, GPid *worker_pid);
d873dd1
+
d873dd1
+static ScSecurityTokenMonitorWorker * sc_security_token_monitor_worker_new (gint write_fd);
d873dd1
+static void sc_security_token_monitor_worker_free (ScSecurityTokenMonitorWorker *worker);
d873dd1
+static void sc_security_token_monitor_worker_die_with_parent (ScSecurityTokenMonitorWorker *worker);
d873dd1
+static gboolean sc_open_pipe (gint *write_fd, gint *read_fd);
d873dd1
+static gboolean sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes);
d873dd1
+static gboolean sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes);
d873dd1
+static ScSecurityToken *sc_read_security_token (gint fd, SECMODModule *module);
d873dd1
+static gboolean sc_write_security_token (gint fd, ScSecurityToken *token);
d873dd1
+
d873dd1
+enum {
d873dd1
+	PROP_0 = 0,
d873dd1
+	PROP_MODULE_PATH,
d873dd1
+	NUMBER_OF_PROPERTIES
d873dd1
+};
d873dd1
+
d873dd1
+enum {
d873dd1
+	SECURITY_TOKEN_INSERTED = 0,
d873dd1
+	SECURITY_TOKEN_REMOVED,
d873dd1
+	ERROR,
d873dd1
+	NUMBER_OF_SIGNALS
d873dd1
+};
d873dd1
+
d873dd1
+static guint sc_security_token_monitor_signals[NUMBER_OF_SIGNALS];
d873dd1
+
d873dd1
+G_DEFINE_TYPE (ScSecurityTokenMonitor, 
d873dd1
+	       sc_security_token_monitor, 
d873dd1
+	       G_TYPE_OBJECT);
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_class_init (ScSecurityTokenMonitorClass *monitor_class)
d873dd1
+{
d873dd1
+    GObjectClass *gobject_class;
d873dd1
+
d873dd1
+    gobject_class = G_OBJECT_CLASS (monitor_class);
d873dd1
+
d873dd1
+    gobject_class->finalize = sc_security_token_monitor_finalize;
d873dd1
+
d873dd1
+    sc_security_token_monitor_class_install_signals (monitor_class);
d873dd1
+    sc_security_token_monitor_class_install_properties (monitor_class);
d873dd1
+
d873dd1
+    g_type_class_add_private (monitor_class,
d873dd1
+			      sizeof (ScSecurityTokenMonitorPrivate));
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_class_install_properties (ScSecurityTokenMonitorClass *token_class)
d873dd1
+{
d873dd1
+    GObjectClass *object_class;
d873dd1
+    GParamSpec *param_spec;
d873dd1
+
d873dd1
+    object_class = G_OBJECT_CLASS (token_class);
d873dd1
+    object_class->set_property = sc_security_token_monitor_set_property;
d873dd1
+    object_class->get_property = sc_security_token_monitor_get_property;
d873dd1
+
d873dd1
+    param_spec = g_param_spec_string ("module-path", _("Module Path"),
d873dd1
+				      _("path to security token PKCS #11 driver"),
d873dd1
+				      NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
d873dd1
+    g_object_class_install_property (object_class, PROP_MODULE_PATH, param_spec);
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_set_property (GObject       *object,
d873dd1
+					guint          prop_id,
d873dd1
+					const GValue  *value,
d873dd1
+					GParamSpec    *pspec)
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitor *monitor = SC_SECURITY_TOKEN_MONITOR (object);
d873dd1
+
d873dd1
+    switch (prop_id)
d873dd1
+    {
d873dd1
+	    case PROP_MODULE_PATH:
d873dd1
+		    sc_security_token_monitor_set_module_path (monitor, 
d873dd1
+							       g_value_get_string (value));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    default:
d873dd1
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
d873dd1
+		    break;
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_get_property (GObject    *object,
d873dd1
+					guint       prop_id,
d873dd1
+					GValue     *value,
d873dd1
+					GParamSpec *pspec)
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitor *monitor = SC_SECURITY_TOKEN_MONITOR (object);
d873dd1
+    gchar *module_path;
d873dd1
+
d873dd1
+    switch (prop_id)
d873dd1
+    {
d873dd1
+	    case PROP_MODULE_PATH:
d873dd1
+		    module_path = sc_security_token_monitor_get_module_path (monitor);
d873dd1
+		    g_value_set_string (value, module_path);
d873dd1
+		    g_free (module_path);
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    default:
d873dd1
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
d873dd1
+		    break;
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+gchar *
d873dd1
+sc_security_token_monitor_get_module_path (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    return monitor->priv->module_path;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_set_module_path (ScSecurityTokenMonitor *monitor,
d873dd1
+					   const gchar            *module_path)
d873dd1
+{
d873dd1
+    if ((monitor->priv->module_path == NULL) && (module_path == NULL))
d873dd1
+	    return;
d873dd1
+
d873dd1
+    if (((monitor->priv->module_path == NULL) ||
d873dd1
+	 (module_path == NULL) ||
d873dd1
+	 (strcmp (monitor->priv->module_path, module_path) != 0))) {
d873dd1
+	    g_free (monitor->priv->module_path);
d873dd1
+	    monitor->priv->module_path = g_strdup (module_path);
d873dd1
+	    g_object_notify (G_OBJECT (monitor), "module-path");
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_token_removed_handler (ScSecurityTokenMonitor *monitor,
d873dd1
+						 ScSecurityToken        *token)
d873dd1
+{
d873dd1
+    sc_debug ("informing security token of its removal");
d873dd1
+    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_REMOVED);
d873dd1
+    sc_debug ("done");
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_token_inserted_handler (ScSecurityTokenMonitor *monitor,
d873dd1
+						  ScSecurityToken        *token)
d873dd1
+{
d873dd1
+    sc_debug ("informing security token of its insertion");
d873dd1
+
d873dd1
+    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_INSERTED);
d873dd1
+    sc_debug ("done");
d873dd1
+
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_class_install_signals (ScSecurityTokenMonitorClass *monitor_class)
d873dd1
+{
d873dd1
+    GObjectClass *object_class;
d873dd1
+
d873dd1
+    object_class = G_OBJECT_CLASS (monitor_class);
d873dd1
+
d873dd1
+    sc_security_token_monitor_signals[SECURITY_TOKEN_INSERTED] =
d873dd1
+	    g_signal_new ("security-token-inserted",
d873dd1
+			  G_OBJECT_CLASS_TYPE (object_class),
d873dd1
+			  G_SIGNAL_RUN_FIRST,
d873dd1
+			  G_STRUCT_OFFSET (ScSecurityTokenMonitorClass,
d873dd1
+					   security_token_inserted), 
d873dd1
+			  NULL, NULL, g_cclosure_marshal_VOID__POINTER, 
d873dd1
+			  G_TYPE_NONE, 1, G_TYPE_POINTER);
d873dd1
+    monitor_class->security_token_inserted = sc_security_token_monitor_token_inserted_handler;
d873dd1
+
d873dd1
+    sc_security_token_monitor_signals[SECURITY_TOKEN_REMOVED] =
d873dd1
+	    g_signal_new ("security-token-removed",
d873dd1
+			  G_OBJECT_CLASS_TYPE (object_class),
d873dd1
+			  G_SIGNAL_RUN_FIRST,
d873dd1
+			  G_STRUCT_OFFSET (ScSecurityTokenMonitorClass,
d873dd1
+					   security_token_removed), 
d873dd1
+			  NULL, NULL, g_cclosure_marshal_VOID__POINTER, 
d873dd1
+			  G_TYPE_NONE, 1, G_TYPE_POINTER);
d873dd1
+    monitor_class->security_token_removed = sc_security_token_monitor_token_removed_handler;
d873dd1
+
d873dd1
+    sc_security_token_monitor_signals[ERROR] =
d873dd1
+	    g_signal_new ("error",
d873dd1
+			  G_OBJECT_CLASS_TYPE (object_class),
d873dd1
+			  G_SIGNAL_RUN_LAST,
d873dd1
+			  G_STRUCT_OFFSET (ScSecurityTokenMonitorClass, error),
d873dd1
+			  NULL, NULL, g_cclosure_marshal_VOID__POINTER,
d873dd1
+			  G_TYPE_NONE, 1, G_TYPE_POINTER);
d873dd1
+    monitor_class->error = NULL;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_slot_id_equal (CK_SLOT_ID *slot_id_1, 
d873dd1
+		  CK_SLOT_ID *slot_id_2)
d873dd1
+{
d873dd1
+    g_assert (slot_id_1 != NULL);
d873dd1
+    g_assert (slot_id_2 != NULL);
d873dd1
+
d873dd1
+    return *slot_id_1 == *slot_id_2;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_slot_id_hash (CK_SLOT_ID *slot_id) 
d873dd1
+{
d873dd1
+    guint32 upper_bits, lower_bits;
d873dd1
+    gint temp;
d873dd1
+
d873dd1
+    if (sizeof (CK_SLOT_ID) == sizeof (gint))
d873dd1
+	    return g_int_hash (slot_id);
d873dd1
+
d873dd1
+    upper_bits = ((*slot_id) >> 31) - 1;
d873dd1
+    lower_bits = (*slot_id) & 0xffffffff;
d873dd1
+
d873dd1
+    /* The upper bits are almost certainly always zero,
d873dd1
+     * so let's degenerate to g_int_hash for the 
d873dd1
+     * (very) common case
d873dd1
+     */
d873dd1
+    temp = lower_bits + upper_bits;
d873dd1
+    return upper_bits + g_int_hash (&temp);
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_init (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    sc_debug ("initializing security token monitor");
d873dd1
+
d873dd1
+    monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
d873dd1
+						 SC_TYPE_SECURITY_TOKEN_MONITOR,
d873dd1
+						 ScSecurityTokenMonitorPrivate);
d873dd1
+    monitor->priv->poll_timeout_id = 0;
d873dd1
+    monitor->priv->is_unstoppable = FALSE;
d873dd1
+    monitor->priv->module = NULL;
d873dd1
+
d873dd1
+    monitor->priv->security_tokens = 
d873dd1
+	    g_hash_table_new_full (g_str_hash, 
d873dd1
+				   g_str_equal,
d873dd1
+				   (GDestroyNotify) g_free, 
d873dd1
+				   (GDestroyNotify) g_object_unref);
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
d873dd1
+    monitor->priv->fds_to_close_on_fork = g_array_new (FALSE, FALSE, sizeof (gint));
d873dd1
+#endif
d873dd1
+
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_finalize (GObject *object)
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitor *monitor;
d873dd1
+    GObjectClass *gobject_class;
d873dd1
+
d873dd1
+    monitor = SC_SECURITY_TOKEN_MONITOR (object);
d873dd1
+    gobject_class =
d873dd1
+	    G_OBJECT_CLASS (sc_security_token_monitor_parent_class);
d873dd1
+
d873dd1
+    sc_security_token_monitor_stop_now (monitor);
d873dd1
+
d873dd1
+    g_hash_table_destroy (monitor->priv->security_tokens);
d873dd1
+    monitor->priv->security_tokens = NULL;
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
d873dd1
+    g_array_free (monitor->priv->fds_to_close_on_fork, TRUE);
d873dd1
+#endif 
d873dd1
+
d873dd1
+    gobject_class->finalize (object);
d873dd1
+}
d873dd1
+
d873dd1
+GQuark 
d873dd1
+sc_security_token_monitor_error_quark (void)
d873dd1
+{
d873dd1
+    static GQuark error_quark = 0;
d873dd1
+
d873dd1
+    if (error_quark == 0)
d873dd1
+	    error_quark = g_quark_from_static_string ("sc-security-token-monitor-error-quark");
d873dd1
+
d873dd1
+    return error_quark;
d873dd1
+}
d873dd1
+
d873dd1
+ScSecurityTokenMonitor *
d873dd1
+sc_security_token_monitor_new (const gchar *module_path)
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitor *instance;
d873dd1
+
d873dd1
+    instance = SC_SECURITY_TOKEN_MONITOR (g_object_new (SC_TYPE_SECURITY_TOKEN_MONITOR, 
d873dd1
+							"module-path", module_path,
d873dd1
+							NULL));
d873dd1
+
d873dd1
+    return instance;
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_emit_error (ScSecurityTokenMonitor *monitor,
d873dd1
+				      GError                 *error)
d873dd1
+{
d873dd1
+    monitor->priv->is_unstoppable = TRUE;
d873dd1
+    g_signal_emit (monitor, sc_security_token_monitor_signals[ERROR], 0,
d873dd1
+		   error);
d873dd1
+    monitor->priv->is_unstoppable = FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_emit_security_token_inserted (ScSecurityTokenMonitor *monitor,
d873dd1
+							ScSecurityToken        *token)
d873dd1
+{
d873dd1
+    monitor->priv->is_unstoppable = TRUE;
d873dd1
+    g_signal_emit (monitor, sc_security_token_monitor_signals[SECURITY_TOKEN_INSERTED], 0,
d873dd1
+		   token);
d873dd1
+    monitor->priv->is_unstoppable = FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_emit_security_token_removed (ScSecurityTokenMonitor *monitor,
d873dd1
+							ScSecurityToken        *token)
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitorState old_state;
d873dd1
+
d873dd1
+    old_state = monitor->priv->state;
d873dd1
+    monitor->priv->is_unstoppable = TRUE;
d873dd1
+    g_signal_emit (monitor, sc_security_token_monitor_signals[SECURITY_TOKEN_REMOVED], 0,
d873dd1
+		   token);
d873dd1
+    monitor->priv->is_unstoppable = FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_monitor_check_for_and_process_events (GIOChannel *io_channel,
d873dd1
+							GIOCondition condition,
d873dd1
+							ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    ScSecurityToken *token;
d873dd1
+    gboolean should_stop;
d873dd1
+    guchar event_type;
d873dd1
+    gchar *token_name;
d873dd1
+    gint fd;
d873dd1
+
d873dd1
+    token = NULL;
d873dd1
+    should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR);
d873dd1
+
d873dd1
+    if (should_stop)
d873dd1
+	    sc_debug ("received %s on event socket, stopping "
d873dd1
+		      "monitor...", 
d873dd1
+		      (condition & G_IO_HUP) && (condition & G_IO_ERR)? 
d873dd1
+		      "error and hangup" : 
d873dd1
+		      (condition & G_IO_HUP)? 
d873dd1
+		      "hangup" : "error");
d873dd1
+
d873dd1
+    if (!(condition & G_IO_IN))
d873dd1
+	    goto out;
d873dd1
+
d873dd1
+    fd = g_io_channel_unix_get_fd (io_channel);
d873dd1
+
d873dd1
+    event_type = '\0';
d873dd1
+    if (!sc_read_bytes (fd, &event_type, 1)) {
d873dd1
+	    should_stop = TRUE;
d873dd1
+	    goto out;
d873dd1
+    }
d873dd1
+
d873dd1
+    token = sc_read_security_token (fd, monitor->priv->module);
d873dd1
+
d873dd1
+    if (token == NULL) {
d873dd1
+	    should_stop = TRUE;
d873dd1
+	    goto out;
d873dd1
+    }
d873dd1
+
d873dd1
+    token_name = sc_security_token_get_name (token);
d873dd1
+
d873dd1
+    switch (event_type) {
d873dd1
+	    case 'I':
d873dd1
+		    g_hash_table_replace (monitor->priv->security_tokens,
d873dd1
+					  token_name, token);
d873dd1
+		    token_name = NULL;
d873dd1
+
d873dd1
+		    sc_security_token_monitor_emit_security_token_inserted (monitor, token);
d873dd1
+		    token = NULL;
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    case 'R':
d873dd1
+		    sc_security_token_monitor_emit_security_token_removed (monitor, token);
d873dd1
+		    if (!g_hash_table_remove (monitor->priv->security_tokens, token_name))
d873dd1
+			    sc_debug ("got removal event of unknown token!");
d873dd1
+		    g_free (token_name);
d873dd1
+		    token_name = NULL;
d873dd1
+		    token = NULL;
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    default: 
d873dd1
+		    g_free (token_name);
d873dd1
+		    token_name = NULL;
d873dd1
+		    g_object_unref (token);
d873dd1
+
d873dd1
+		    should_stop = TRUE;
d873dd1
+		    break;
d873dd1
+    }
d873dd1
+
d873dd1
+out:
d873dd1
+    if (should_stop) {
d873dd1
+	    GError *error;
d873dd1
+
d873dd1
+	    error = g_error_new (SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+				 SC_SECURITY_TOKEN_MONITOR_ERROR_WATCHING_FOR_EVENTS,
d873dd1
+				 "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source"));
d873dd1
+
d873dd1
+	    sc_security_token_monitor_emit_error (monitor, error);
d873dd1
+	    g_error_free (error);
d873dd1
+	    sc_security_token_monitor_stop_now (monitor);
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_event_processing_stopped_handler (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    monitor->priv->security_token_event_source = NULL;
d873dd1
+    sc_security_token_monitor_stop_now (monitor);
d873dd1
+}
d873dd1
+
d873dd1
+/* sorta complex function that is nothing more than fork() without having
d873dd1
+ * to worry about reaping the child later with waitpid
d873dd1
+ */
d873dd1
+static GPid
d873dd1
+sc_fork_and_disown (void)
d873dd1
+{
d873dd1
+    pid_t child_pid;
d873dd1
+    GPid grandchild_pid;
d873dd1
+    gint write_fd, read_fd;
d873dd1
+    gint saved_errno;
d873dd1
+
d873dd1
+    write_fd = -1;
d873dd1
+    read_fd = -1;
d873dd1
+    if (!sc_open_pipe (&write_fd, &read_fd))
d873dd1
+	    return (GPid) -1;
d873dd1
+
d873dd1
+    child_pid = fork ();
d873dd1
+
d873dd1
+    if (child_pid < 0) {
d873dd1
+	    close (write_fd);
d873dd1
+	    close (read_fd);
d873dd1
+	    return (GPid) child_pid;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (child_pid == 0) {
d873dd1
+
d873dd1
+	    /* close the end of the pipe we're not going to use
d873dd1
+	     */
d873dd1
+	    close (read_fd);
d873dd1
+
d873dd1
+	    /* fork again 
d873dd1
+	     */
d873dd1
+	    child_pid = fork ();
d873dd1
+
d873dd1
+	    /* in the event of error, write out negative errno
d873dd1
+	     */
d873dd1
+	    if (child_pid < 0) {
d873dd1
+		    child_pid = -1 * errno;
d873dd1
+
d873dd1
+		    sc_write_bytes (write_fd, &child_pid, sizeof (child_pid));
d873dd1
+		    close (write_fd);
d873dd1
+		    _exit (1);
d873dd1
+	    }
d873dd1
+
d873dd1
+	    /* otherwise write out the pid of the child and exit
d873dd1
+	     */
d873dd1
+	    if (child_pid != 0) {
d873dd1
+
d873dd1
+		    signal (SIGPIPE, SIG_IGN);
d873dd1
+
d873dd1
+		    if (!sc_write_bytes (write_fd, &child_pid, sizeof (child_pid))) {
d873dd1
+			    kill (SIGKILL, child_pid);
d873dd1
+			    _exit (2);
d873dd1
+		    }
d873dd1
+		    close (write_fd);
d873dd1
+		    _exit (0);
d873dd1
+	    }
d873dd1
+	    close (write_fd);
d873dd1
+
d873dd1
+	    /* we're done, we've forked without having to worry about
d873dd1
+	     * reaping the child later
d873dd1
+	     */
d873dd1
+	    g_assert (child_pid == 0);
d873dd1
+	    return (GPid) 0;
d873dd1
+    }
d873dd1
+
d873dd1
+    /* close the end of the pipe we're not going to use
d873dd1
+     */
d873dd1
+    close (write_fd);
d873dd1
+
d873dd1
+    grandchild_pid = -1;
d873dd1
+    if (!sc_read_bytes (read_fd, &grandchild_pid, sizeof (grandchild_pid))) {
d873dd1
+	    grandchild_pid = -1;
d873dd1
+    }
d873dd1
+
d873dd1
+    saved_errno = errno;
d873dd1
+
d873dd1
+    /* close the other end of the pipe since we're done with it
d873dd1
+     */
d873dd1
+    close (read_fd);
d873dd1
+
d873dd1
+    /* wait for child to die (and emancipate the grandchild)
d873dd1
+     */
d873dd1
+    waitpid (child_pid, NULL, 0);
d873dd1
+    
d873dd1
+    errno = saved_errno;
d873dd1
+    return (GPid) grandchild_pid;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_open_pipe (gint *write_fd,
d873dd1
+	      gint *read_fd)
d873dd1
+{
d873dd1
+    gint pipe_fds[2] = { -1, -1 };
d873dd1
+
d873dd1
+    g_assert (write_fd != NULL);
d873dd1
+    g_assert (read_fd != NULL);
d873dd1
+
d873dd1
+    if (pipe (pipe_fds) < 0)
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) {
d873dd1
+	    close (pipe_fds[0]);
d873dd1
+	    close (pipe_fds[1]);
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) {
d873dd1
+	    close (pipe_fds[0]);
d873dd1
+	    close (pipe_fds[1]);
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    *read_fd = pipe_fds[0];
d873dd1
+    *write_fd = pipe_fds[1];
d873dd1
+ 
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_stop_watching_for_events (ScSecurityTokenMonitor  *monitor)
d873dd1
+{
d873dd1
+    if (monitor->priv->security_token_event_source != NULL) {
d873dd1
+	    g_source_destroy (monitor->priv->security_token_event_source);
d873dd1
+	    monitor->priv->security_token_event_source = NULL;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (monitor->priv->security_token_event_watcher_pid > 0) {
d873dd1
+	    kill (monitor->priv->security_token_event_watcher_pid, SIGKILL);
d873dd1
+	    monitor->priv->security_token_event_watcher_pid = 0;
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_load_nss (GError **error)
d873dd1
+{
d873dd1
+    SECStatus status = SECSuccess;
d873dd1
+    static const guint32 flags = 
d873dd1
+	NSS_INIT_READONLY| NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | 
d873dd1
+	NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT | 
d873dd1
+	NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD;
d873dd1
+
d873dd1
+    sc_debug ("attempting to load NSS database '%s'",
d873dd1
+	      SC_SECURITY_TOKEN_MONITOR_NSS_DB);
d873dd1
+
d873dd1
+    status = NSS_Initialize (SC_SECURITY_TOKEN_MONITOR_NSS_DB,
d873dd1
+			     "", "", SECMOD_DB, flags);
d873dd1
+
d873dd1
+    if (status != SECSuccess) {
d873dd1
+	    gsize error_message_size;
d873dd1
+	    gchar *error_message;
d873dd1
+
d873dd1
+	    error_message_size = PR_GetErrorTextLength ();
d873dd1
+
d873dd1
+	    if (error_message_size == 0) {
d873dd1
+		    sc_debug ("NSS security system could not be initialized");
d873dd1
+		    g_set_error (error,
d873dd1
+				 SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+				 SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
d873dd1
+				 _("NSS security system could not be initialized"));
d873dd1
+		    goto out;
d873dd1
+	    }
d873dd1
+
d873dd1
+	    error_message = g_slice_alloc0 (error_message_size);
d873dd1
+	    PR_GetErrorText (error_message);
d873dd1
+
d873dd1
+	    g_set_error (error,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
d873dd1
+			 "%s", error_message);
d873dd1
+	    sc_debug ("NSS security system could not be initialized - %s",
d873dd1
+		      error_message);
d873dd1
+
d873dd1
+	    g_slice_free1 (error_message_size, error_message);
d873dd1
+
d873dd1
+	    goto out;
d873dd1
+    }
d873dd1
+
d873dd1
+    sc_debug ("NSS database sucessfully loaded");
d873dd1
+    return TRUE;
d873dd1
+
d873dd1
+out:
d873dd1
+    sc_debug ("NSS database couldn't be sucessfully loaded");
d873dd1
+    return FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static SECMODModule *
d873dd1
+sc_load_driver (gchar   *module_path,
d873dd1
+		GError **error)
d873dd1
+{
d873dd1
+    SECMODModule *module;
d873dd1
+    gchar *module_spec;
d873dd1
+    gboolean module_explicitly_specified;
d873dd1
+
d873dd1
+    sc_debug ("attempting to load driver...");
d873dd1
+
d873dd1
+    module = NULL;
d873dd1
+    module_explicitly_specified = module_path != NULL;
d873dd1
+    if (module_explicitly_specified) {
d873dd1
+	    module_spec = g_strdup_printf ("library=\"%s\"", module_path);
d873dd1
+	    sc_debug ("loading security token driver using spec '%s'",
d873dd1
+		      module_spec);
d873dd1
+
d873dd1
+	    module = SECMOD_LoadUserModule (module_spec, 
d873dd1
+					    NULL /* parent */, 
d873dd1
+					    FALSE /* recurse */);
d873dd1
+	    g_free (module_spec);
d873dd1
+	    module_spec = NULL;
d873dd1
+
d873dd1
+    } else {
d873dd1
+	    SECMODModuleList *modules, *tmp;
d873dd1
+
d873dd1
+	    modules = SECMOD_GetDefaultModuleList ();
d873dd1
+
d873dd1
+	    for (tmp = modules; tmp != NULL; tmp = tmp->next) {
d873dd1
+		    if (!SECMOD_HasRemovableSlots (tmp->module) ||
d873dd1
+			!tmp->module->loaded)
d873dd1
+			    continue;
d873dd1
+
d873dd1
+		    module = SECMOD_ReferenceModule (tmp->module);
d873dd1
+		    break;
d873dd1
+	    }
d873dd1
+
d873dd1
+	    /* fallback to compiled in driver path
d873dd1
+	     */
d873dd1
+	    if (module == NULL) {
d873dd1
+		    if (g_file_test (SC_SECURITY_TOKEN_MONITOR_DRIVER,
d873dd1
+				     G_FILE_TEST_IS_REGULAR)) {
d873dd1
+
d873dd1
+			    module_spec = g_strdup_printf ("library=\"%s\"", module_path);
d873dd1
+			    sc_debug ("loading security token driver using spec '%s'",
d873dd1
+				      module_spec);
d873dd1
+
d873dd1
+			    module = SECMOD_LoadUserModule (module_spec, 
d873dd1
+							    NULL /* parent */, 
d873dd1
+							    FALSE /* recurse */);
d873dd1
+			    g_free (module_spec);
d873dd1
+			    module_spec = NULL;
d873dd1
+
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+
d873dd1
+    }
d873dd1
+
d873dd1
+    if (!module_explicitly_specified && module == NULL) {
d873dd1
+	    g_set_error (error,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
d873dd1
+			 _("no suitable security token driver could be found"));
d873dd1
+    } else if (module == NULL || !module->loaded) {
d873dd1
+
d873dd1
+	    gsize error_message_size;
d873dd1
+	    gchar *error_message;
d873dd1
+            
d873dd1
+	    if (module != NULL && !module->loaded) {
d873dd1
+		    sc_debug ("module found but not loaded?!");
d873dd1
+		    SECMOD_DestroyModule (module);
d873dd1
+		    module = NULL;
d873dd1
+	    }
d873dd1
+
d873dd1
+	    error_message_size = PR_GetErrorTextLength ();
d873dd1
+
d873dd1
+	    if (error_message_size == 0) {
d873dd1
+		    sc_debug ("security token driver '%s' could not be loaded",
d873dd1
+			      module_path);
d873dd1
+		    g_set_error (error,
d873dd1
+				 SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+				 SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
d873dd1
+				 _("security token driver '%s' could not be "
d873dd1
+				   "loaded"), module_path);
d873dd1
+		    goto out;
d873dd1
+	    }
d873dd1
+
d873dd1
+	    error_message = g_slice_alloc0 (error_message_size);
d873dd1
+	    PR_GetErrorText (error_message);
d873dd1
+
d873dd1
+	    g_set_error (error,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
d873dd1
+			 "%s", error_message);
d873dd1
+
d873dd1
+	    sc_debug ("security token driver '%s' could not be loaded - %s",
d873dd1
+		      module_path, error_message);
d873dd1
+	    g_slice_free1 (error_message_size, error_message);
d873dd1
+    }
d873dd1
+
d873dd1
+out:
d873dd1
+    return module;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_get_all_tokens (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    int i;
d873dd1
+
d873dd1
+    for (i = 0; i < monitor->priv->module->slotCount; i++) {
d873dd1
+	    ScSecurityToken *token;
d873dd1
+	    CK_SLOT_ID    slot_id;
d873dd1
+	    gint          slot_series;
d873dd1
+	    gchar *token_name;
d873dd1
+
d873dd1
+	    slot_id = PK11_GetSlotID (monitor->priv->module->slots[i]);
d873dd1
+	    slot_series = PK11_GetSlotSeries (monitor->priv->module->slots[i]);
d873dd1
+
d873dd1
+	    token = _sc_security_token_new (monitor->priv->module, 
d873dd1
+					    slot_id, slot_series);
d873dd1
+
d873dd1
+	    token_name = sc_security_token_get_name (token);
d873dd1
+
d873dd1
+	    g_hash_table_replace (monitor->priv->security_tokens,
d873dd1
+				  token_name, token);
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+gboolean
d873dd1
+sc_security_token_monitor_start (ScSecurityTokenMonitor  *monitor,
d873dd1
+				 GError                 **error)
d873dd1
+{
d873dd1
+    GError *watching_error;
d873dd1
+    gint worker_fd;
d873dd1
+    GPid worker_pid;
d873dd1
+    GIOChannel *io_channel;
d873dd1
+    GSource *source;
d873dd1
+    GIOFlags channel_flags;
d873dd1
+    GError *nss_error;
d873dd1
+
d873dd1
+    if (monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STARTED) {
d873dd1
+	    sc_debug ("security token monitor already started");
d873dd1
+	    return TRUE;
d873dd1
+    }
d873dd1
+
d873dd1
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STARTING;
d873dd1
+
d873dd1
+    worker_fd = -1;
d873dd1
+    worker_pid = 0;
d873dd1
+
d873dd1
+    nss_error = NULL;
d873dd1
+    if (!monitor->priv->nss_is_loaded && !sc_load_nss (&nss_error)) {
d873dd1
+	    g_propagate_error (error, nss_error);
d873dd1
+	    goto out;
d873dd1
+    }
d873dd1
+    monitor->priv->nss_is_loaded = TRUE;
d873dd1
+
d873dd1
+    if (monitor->priv->module == NULL)
d873dd1
+	    monitor->priv->module = sc_load_driver (monitor->priv->module_path, &nss_error);
d873dd1
+
d873dd1
+    if (monitor->priv->module == NULL) {
d873dd1
+	    g_propagate_error (error, nss_error);
d873dd1
+	    goto out;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (!sc_security_token_monitor_create_worker (monitor, &worker_fd, &worker_pid)) {
d873dd1
+
d873dd1
+	    g_set_error (error,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_WATCHING_FOR_EVENTS,
d873dd1
+			 _("could not watch for incoming token events - %s"),
d873dd1
+			 g_strerror (errno));
d873dd1
+
d873dd1
+	    goto out;
d873dd1
+    }
d873dd1
+
d873dd1
+    monitor->priv->security_token_event_watcher_pid = worker_pid;
d873dd1
+
d873dd1
+    io_channel = g_io_channel_unix_new (worker_fd);
d873dd1
+
d873dd1
+    channel_flags = g_io_channel_get_flags (io_channel);
d873dd1
+    watching_error = NULL;
d873dd1
+
d873dd1
+    source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP);
d873dd1
+    g_io_channel_unref (io_channel);
d873dd1
+    io_channel = NULL;
d873dd1
+
d873dd1
+    monitor->priv->security_token_event_source = source;
d873dd1
+
d873dd1
+    g_source_set_callback (monitor->priv->security_token_event_source,
d873dd1
+			   (GSourceFunc) (GIOFunc)
d873dd1
+			   sc_security_token_monitor_check_for_and_process_events,
d873dd1
+			   monitor,
d873dd1
+			   (GDestroyNotify)
d873dd1
+			   sc_security_token_monitor_event_processing_stopped_handler);
d873dd1
+    g_source_attach (monitor->priv->security_token_event_source, NULL);
d873dd1
+    g_source_unref (monitor->priv->security_token_event_source);
d873dd1
+
d873dd1
+    /* populate the hash with tokens that are already inserted
d873dd1
+     */
d873dd1
+    sc_security_token_monitor_get_all_tokens (monitor);
d873dd1
+
d873dd1
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STARTED;
d873dd1
+
d873dd1
+out:
d873dd1
+    /* don't leave it in a half started state
d873dd1
+     */
d873dd1
+    if (monitor->priv->state != SC_SECURITY_TOKEN_MONITOR_STATE_STARTED) {
d873dd1
+	    sc_debug ("security token monitor could not be completely started");
d873dd1
+	    sc_security_token_monitor_stop (monitor);
d873dd1
+    } else
d873dd1
+	    sc_debug ("security token monitor started");
d873dd1
+
d873dd1
+    return monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STARTED;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_monitor_stop_now (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    if (monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED)
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED;
d873dd1
+    sc_security_token_monitor_stop_watching_for_events (monitor);
d873dd1
+#ifdef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
d873dd1
+    if (monitor->priv->module != NULL) {
d873dd1
+	    SECMOD_DestroyModule (monitor->priv->module);
d873dd1
+	    monitor->priv->module = NULL;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (monitor->priv->nss_is_loaded) {
d873dd1
+	    NSS_Shutdown ();
d873dd1
+	    monitor->priv->nss_is_loaded = FALSE;
d873dd1
+    }
d873dd1
+#endif
d873dd1
+    sc_debug ("security token monitor stopped");
d873dd1
+    
d873dd1
+    return FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_queue_stop (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+
d873dd1
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STOPPING;
d873dd1
+
d873dd1
+    g_idle_add ((GSourceFunc) sc_security_token_monitor_stop_now, monitor);
d873dd1
+}
d873dd1
+
d873dd1
+void 
d873dd1
+sc_security_token_monitor_stop (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    if (monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED)
d873dd1
+	    return;
d873dd1
+
d873dd1
+    if (monitor->priv->is_unstoppable) {
d873dd1
+	    sc_security_token_monitor_queue_stop (monitor);
d873dd1
+	    return;
d873dd1
+    } 
d873dd1
+
d873dd1
+    sc_security_token_monitor_stop_now (monitor);
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_check_for_login_token (CK_SLOT_ID       slot_id,
d873dd1
+						 ScSecurityToken *token,
d873dd1
+						 gboolean        *is_inserted)
d873dd1
+{
d873dd1
+    g_assert (is_inserted != NULL);
d873dd1
+
d873dd1
+    if (sc_security_token_is_login_token (token))
d873dd1
+	    *is_inserted = TRUE;
d873dd1
+
d873dd1
+}
d873dd1
+
d873dd1
+gboolean 
d873dd1
+sc_security_token_monitor_login_token_is_inserted (ScSecurityTokenMonitor *monitor) 
d873dd1
+
d873dd1
+{
d873dd1
+    gboolean is_inserted;
d873dd1
+
d873dd1
+    is_inserted = FALSE;
d873dd1
+    g_hash_table_foreach (monitor->priv->security_tokens, 
d873dd1
+			  (GHFunc)
d873dd1
+			  sc_security_token_monitor_check_for_login_token,
d873dd1
+			  &is_inserted);
d873dd1
+    return is_inserted;
d873dd1
+}
d873dd1
+
d873dd1
+static gint
d873dd1
+sc_get_max_open_fds (void)
d873dd1
+{
d873dd1
+  struct rlimit open_fd_limit;
d873dd1
+  const gint fallback_limit = SC_MAX_OPEN_FILE_DESCRIPTORS;
d873dd1
+
d873dd1
+  if (getrlimit (RLIMIT_NOFILE, &open_fd_limit) < 0) {
d873dd1
+	  sc_debug ("could not get file descriptor limit: %s",
d873dd1
+		    g_strerror (errno));
d873dd1
+	  sc_debug ("returning fallback file descriptor limit of %d",
d873dd1
+		    fallback_limit);
d873dd1
+	  return fallback_limit;
d873dd1
+  }
d873dd1
+
d873dd1
+  if (open_fd_limit.rlim_cur == RLIM_INFINITY) {
d873dd1
+	  sc_debug ("currently no file descriptor limit, returning fallback limit of %d",
d873dd1
+		    fallback_limit);
d873dd1
+	  return fallback_limit;
d873dd1
+  }
d873dd1
+
d873dd1
+  return (gint) open_fd_limit.rlim_cur;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_close_all_fds (int *fds_to_keep_open)
d873dd1
+{
d873dd1
+    int max_open_fds, fd;
d873dd1
+
d873dd1
+    sc_debug ("closing all file descriptors");
d873dd1
+    max_open_fds = sc_get_max_open_fds ();
d873dd1
+
d873dd1
+    for (fd = 0; fd < max_open_fds; fd++) {
d873dd1
+	    int i;
d873dd1
+	    gboolean should_close_fd;
d873dd1
+
d873dd1
+	    should_close_fd = TRUE;
d873dd1
+
d873dd1
+	    if (fds_to_keep_open != NULL) {
d873dd1
+		    for (i = 0; fds_to_keep_open[i] >= 0; i++) {
d873dd1
+			    if (fd == fds_to_keep_open[i]) {
d873dd1
+				    should_close_fd = FALSE;
d873dd1
+				    break;
d873dd1
+			    }
d873dd1
+		    }
d873dd1
+	    } 
d873dd1
+
d873dd1
+	    if (should_close_fd) {
d873dd1
+		    sc_debug ("closing file descriptor '%d'", fd);
d873dd1
+		    close (fd);
d873dd1
+	    }
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_close_open_fds (int *fds_to_keep_open)
d873dd1
+{
d873dd1
+    /* using DIR instead of GDir because we need access to dirfd so
d873dd1
+     * that we can iterate through the fds and close them in one sweep.
d873dd1
+     * (if we just closed all of them then we would close the one we're using
d873dd1
+     * for reading the directory!)
d873dd1
+     */
d873dd1
+    DIR *dir;
d873dd1
+    struct dirent *entry;
d873dd1
+    gint fd, opendir_fd;
d873dd1
+    gboolean should_use_fallback;
d873dd1
+
d873dd1
+    should_use_fallback = FALSE;
d873dd1
+    opendir_fd = -1;
d873dd1
+
d873dd1
+    dir = opendir (SC_OPEN_FILE_DESCRIPTORS_DIR);
d873dd1
+
d873dd1
+    if (dir != NULL)
d873dd1
+	    opendir_fd = dirfd (dir);
d873dd1
+
d873dd1
+    if ((dir == NULL) || (opendir_fd < 0)) {
d873dd1
+	    sc_debug ("could not open "SC_OPEN_FILE_DESCRIPTORS_DIR": %s", g_strerror (errno));
d873dd1
+	    should_use_fallback = TRUE;
d873dd1
+    } else {
d873dd1
+	    sc_debug ("reading files in '"SC_OPEN_FILE_DESCRIPTORS_DIR"'");
d873dd1
+	    while ((entry = readdir (dir)) != NULL) {
d873dd1
+		    gint i;
d873dd1
+		    glong filename_as_number;
d873dd1
+		    gchar *byte_after_number;
d873dd1
+		    gboolean should_close_fd;
d873dd1
+
d873dd1
+		    errno = 0;
d873dd1
+		    if (entry->d_name[0] == '.')
d873dd1
+			    continue;
d873dd1
+
d873dd1
+		    sc_debug ("scanning filename '%s' for file descriptor number",
d873dd1
+			       entry->d_name);
d873dd1
+		    fd = -1;
d873dd1
+		    filename_as_number = strtol (entry->d_name, &byte_after_number, 10);
d873dd1
+
d873dd1
+		    g_assert (byte_after_number != NULL);
d873dd1
+
d873dd1
+		    if ((*byte_after_number != '\0') ||
d873dd1
+			(filename_as_number < 0) ||
d873dd1
+			(filename_as_number >= G_MAXINT)) {
d873dd1
+			    sc_debug ("filename '%s' does not appear to represent a "
d873dd1
+				       "file descriptor: %s",
d873dd1
+				       entry->d_name, strerror (errno));
d873dd1
+			    should_use_fallback = TRUE;
d873dd1
+		    } else {
d873dd1
+			    fd = (gint) filename_as_number;
d873dd1
+			    sc_debug ("filename '%s' represents file descriptor '%d'",
d873dd1
+				       entry->d_name, fd);
d873dd1
+			    should_use_fallback = FALSE;
d873dd1
+		    }
d873dd1
+
d873dd1
+		    if (fd == opendir_fd) {
d873dd1
+			    should_close_fd = FALSE;
d873dd1
+		    } else {
d873dd1
+			    should_close_fd = TRUE;
d873dd1
+			    if (fds_to_keep_open != NULL)
d873dd1
+				    for (i = 0; fds_to_keep_open[i] >= 0; i++) {
d873dd1
+					    if (fd == fds_to_keep_open[i]) {
d873dd1
+						    should_close_fd = FALSE;
d873dd1
+						    break;
d873dd1
+					    }
d873dd1
+				    }
d873dd1
+		    }
d873dd1
+
d873dd1
+		    if (should_close_fd) {
d873dd1
+			    sc_debug ("closing file descriptor '%d'", fd);
d873dd1
+			    close (fd);
d873dd1
+		    } else {
d873dd1
+			    sc_debug ("will not close file descriptor '%d' because it "
d873dd1
+				      "is still needed", fd);
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+	    
d873dd1
+	    if (entry != NULL)
d873dd1
+		    should_use_fallback = TRUE;
d873dd1
+
d873dd1
+	    sc_debug ("closing directory '"SC_OPEN_FILE_DESCRIPTORS_DIR"'");
d873dd1
+	    closedir (dir);
d873dd1
+    }
d873dd1
+
d873dd1
+    /* if /proc isn't mounted or something else is screwy,
d873dd1
+     * fall back to closing everything
d873dd1
+     */
d873dd1
+    if (should_use_fallback)
d873dd1
+	    sc_close_all_fds (fds_to_keep_open);
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_close_fds (gint *fds, 
d873dd1
+	      gint  num_fds)
d873dd1
+{
d873dd1
+    gint i;
d873dd1
+
d873dd1
+    for (i = 0; i < num_fds; i++)
d873dd1
+	    close (fds[i]);
d873dd1
+}
d873dd1
+
d873dd1
+static ScSecurityTokenMonitorWorker *
d873dd1
+sc_security_token_monitor_worker_new (gint write_fd)
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitorWorker *worker;
d873dd1
+
d873dd1
+    worker = g_slice_new0 (ScSecurityTokenMonitorWorker);
d873dd1
+    worker->write_fd = write_fd;
d873dd1
+    worker->module = NULL;
d873dd1
+
d873dd1
+    worker->security_tokens =
d873dd1
+	    g_hash_table_new_full ((GHashFunc) sc_slot_id_hash, 
d873dd1
+				   (GEqualFunc) sc_slot_id_equal, 
d873dd1
+				   (GDestroyNotify) g_free, 
d873dd1
+				   (GDestroyNotify) g_object_unref);
d873dd1
+
d873dd1
+    return worker;
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_monitor_worker_free (ScSecurityTokenMonitorWorker *worker)
d873dd1
+{
d873dd1
+    if (worker->security_tokens != NULL) {
d873dd1
+	    g_hash_table_destroy (worker->security_tokens);
d873dd1
+	    worker->security_tokens = NULL;
d873dd1
+    }
d873dd1
+
d873dd1
+    g_slice_free (ScSecurityTokenMonitorWorker, worker);
d873dd1
+}
d873dd1
+
d873dd1
+/* This function checks to see if the helper's connection to the
d873dd1
+ * parent process has been closed.  If it has, we assume the
d873dd1
+ * parent has died (or is otherwise done with the connection)
d873dd1
+ * and so we die, too.  We do this from a signal handler (yuck!)
d873dd1
+ * because there isn't a nice way to cancel the 
d873dd1
+ * SECMOD_WaitForAnyTokenEvent call, which just sits and blocks
d873dd1
+ * indefinitely.  There is a SECMOD_CancelWait wait function
d873dd1
+ * that we could call if we would have gone multithreaded like
d873dd1
+ * NSS really wants us to do, but that call isn't signal handler
d873dd1
+ * safe, so we just _exit() instead (eww).
d873dd1
+ */
d873dd1
+static void
d873dd1
+worker_io_signal_handler (int        signal_number, 
d873dd1
+			  siginfo_t *signal_info,
d873dd1
+			  void      *data)
d873dd1
+{
d873dd1
+    int number_of_events;
d873dd1
+    int old_errno;
d873dd1
+    struct pollfd poll_fds[1] = { { 0 } };
d873dd1
+    int parent_fd;
d873dd1
+
d873dd1
+    old_errno = errno;
d873dd1
+
d873dd1
+    /* pipe fd set up to talk to the parent */
d873dd1
+    parent_fd = signal_info->si_fd;
d873dd1
+
d873dd1
+    /* We only care about disconnection events
d873dd1
+     * (which get unmasked implicitly), so we just
d873dd1
+     * pass 0 for the event mask
d873dd1
+     */
d873dd1
+    poll_fds[0].events = 0;
d873dd1
+    poll_fds[0].fd = parent_fd;
d873dd1
+    
d873dd1
+    do {
d873dd1
+	    number_of_events = poll (poll_fds, G_N_ELEMENTS (poll_fds), 0);
d873dd1
+    } while ((number_of_events < 0) && (errno == EINTR));
d873dd1
+
d873dd1
+    g_assert (number_of_events <= G_N_ELEMENTS (poll_fds));
d873dd1
+
d873dd1
+    if (number_of_events < 0)
d873dd1
+	    _exit (errno);
d873dd1
+
d873dd1
+    /* pipe disconnected; parent died
d873dd1
+     */
d873dd1
+    if (number_of_events > 0) {
d873dd1
+	    g_assert (!(poll_fds[0].revents & POLLNVAL));
d873dd1
+
d873dd1
+	    if ((poll_fds[0].revents & POLLHUP) ||
d873dd1
+		(poll_fds[0].revents & POLLERR)) {
d873dd1
+		    _exit (poll_fds[0].revents);
d873dd1
+	    }
d873dd1
+    } 
d873dd1
+
d873dd1
+    errno = old_errno;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_monitor_worker_die_with_parent (ScSecurityTokenMonitorWorker *worker)
d873dd1
+{
d873dd1
+    struct sigaction action = { { 0 } };
d873dd1
+    gint flags;
d873dd1
+
d873dd1
+    /* dirty hack to clean up worker if parent goes away
d873dd1
+     */
d873dd1
+    sigemptyset (&action.sa_mask);
d873dd1
+    action.sa_sigaction = worker_io_signal_handler;
d873dd1
+    action.sa_flags = SA_SIGINFO;
d873dd1
+    sigaction (SIGIO, &action, NULL);
d873dd1
+
d873dd1
+    flags = fcntl (worker->write_fd, F_GETFL, 0);
d873dd1
+
d873dd1
+    fcntl (worker->write_fd, F_SETOWN, getpid ());
d873dd1
+    fcntl (worker->write_fd, F_SETFL, flags | O_ASYNC);
d873dd1
+    fcntl (worker->write_fd, F_SETSIG, SIGIO);
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes)
d873dd1
+{
d873dd1
+    size_t bytes_left;
d873dd1
+    size_t total_bytes_read;
d873dd1
+    ssize_t bytes_read;
d873dd1
+
d873dd1
+    bytes_left = (size_t) num_bytes;
d873dd1
+    total_bytes_read = 0;
d873dd1
+
d873dd1
+    do {
d873dd1
+	    bytes_read = read (fd, bytes + total_bytes_read, bytes_left);
d873dd1
+	    g_assert (bytes_read <= (ssize_t) bytes_left);
d873dd1
+
d873dd1
+	    if (bytes_read <= 0) {
d873dd1
+		    if ((bytes_read < 0) && (errno == EINTR || errno == EAGAIN))
d873dd1
+			    continue;
d873dd1
+
d873dd1
+		    bytes_left = 0;
d873dd1
+	    } else {
d873dd1
+		    bytes_left -= bytes_read;
d873dd1
+		    total_bytes_read += bytes_read;
d873dd1
+	    }
d873dd1
+    } while (bytes_left > 0);
d873dd1
+
d873dd1
+    if (total_bytes_read <  (size_t) num_bytes)
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes)
d873dd1
+{
d873dd1
+    size_t bytes_left;
d873dd1
+    size_t total_bytes_written;
d873dd1
+    ssize_t bytes_written;
d873dd1
+
d873dd1
+    bytes_left = (size_t) num_bytes;
d873dd1
+    total_bytes_written = 0;
d873dd1
+
d873dd1
+    do {
d873dd1
+	    bytes_written = write (fd, bytes + total_bytes_written, bytes_left);
d873dd1
+	    g_assert (bytes_written <= (ssize_t) bytes_left);
d873dd1
+
d873dd1
+	    if (bytes_written <= 0) {
d873dd1
+		    if ((bytes_written < 0) && (errno == EINTR || errno == EAGAIN))
d873dd1
+			    continue;
d873dd1
+
d873dd1
+		    bytes_left = 0;
d873dd1
+	    } else {
d873dd1
+		    bytes_left -= bytes_written;
d873dd1
+		    total_bytes_written += bytes_written;
d873dd1
+	    }
d873dd1
+    } while (bytes_left > 0);
d873dd1
+
d873dd1
+    if (total_bytes_written <  (size_t) num_bytes)
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static ScSecurityToken *
d873dd1
+sc_read_security_token (gint fd, SECMODModule *module)
d873dd1
+{
d873dd1
+    ScSecurityToken *token;
d873dd1
+    gchar *token_name;
d873dd1
+    gsize token_name_size;
d873dd1
+
d873dd1
+    token_name_size = 0;
d873dd1
+    if (!sc_read_bytes (fd, &token_name_size, sizeof (token_name_size)))
d873dd1
+	return NULL;
d873dd1
+
d873dd1
+    token_name = g_slice_alloc0 (token_name_size);
d873dd1
+    if (!sc_read_bytes (fd, token_name, token_name_size)) {
d873dd1
+	    g_slice_free1 (token_name_size, token_name);
d873dd1
+	    return NULL;
d873dd1
+    }
d873dd1
+    token = _sc_security_token_new_from_name (module, token_name);
d873dd1
+    g_slice_free1 (token_name_size, token_name);
d873dd1
+
d873dd1
+    return token;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_write_security_token (gint             fd, 
d873dd1
+			 ScSecurityToken *token)
d873dd1
+{
d873dd1
+    gsize token_name_size;
d873dd1
+    gchar *token_name;
d873dd1
+
d873dd1
+    token_name = sc_security_token_get_name (token);
d873dd1
+    token_name_size = strlen (token_name) + 1;
d873dd1
+
d873dd1
+    if (!sc_write_bytes (fd, &token_name_size, sizeof (token_name_size))) {
d873dd1
+	g_free (token_name);
d873dd1
+	return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (!sc_write_bytes (fd, token_name, token_name_size)) {
d873dd1
+	g_free (token_name);
d873dd1
+	return FALSE;
d873dd1
+    }
d873dd1
+    g_free (token_name);
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_monitor_worker_emit_security_token_removed (ScSecurityTokenMonitorWorker  *worker, 
d873dd1
+							      ScSecurityToken               *token, 
d873dd1
+							      GError                       **error)
d873dd1
+{
d873dd1
+    sc_debug ("token '%s' removed!", sc_security_token_get_name (token));
d873dd1
+
d873dd1
+    if (!sc_write_bytes (worker->write_fd, "R", 1)) 
d873dd1
+	    goto error_out;
d873dd1
+
d873dd1
+    if (!sc_write_security_token (worker->write_fd, token))
d873dd1
+	    goto error_out;
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+
d873dd1
+error_out:
d873dd1
+    g_set_error (error, SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+		 SC_SECURITY_TOKEN_MONITOR_ERROR_REPORTING_EVENTS, 
d873dd1
+		 "%s", g_strerror (errno));
d873dd1
+    return FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_monitor_worker_emit_security_token_inserted (ScSecurityTokenMonitorWorker  *worker, 
d873dd1
+							       ScSecurityToken               *token,
d873dd1
+							       GError                       **error)
d873dd1
+{
d873dd1
+    GError *write_error;
d873dd1
+
d873dd1
+    write_error = NULL;
d873dd1
+    sc_debug ("token '%s' inserted!", sc_security_token_get_name (token));
d873dd1
+    if (!sc_write_bytes (worker->write_fd, "I", 1)) 
d873dd1
+	    goto error_out;
d873dd1
+
d873dd1
+    if (!sc_write_security_token (worker->write_fd, token))
d873dd1
+	    goto error_out;
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+
d873dd1
+error_out:
d873dd1
+    g_set_error (error, SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+		 SC_SECURITY_TOKEN_MONITOR_ERROR_REPORTING_EVENTS, 
d873dd1
+		 "%s", g_strerror (errno));
d873dd1
+    return FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_monitor_worker_watch_for_and_process_event (ScSecurityTokenMonitorWorker *worker,
d873dd1
+							      GError                      **error)
d873dd1
+{
d873dd1
+    PK11SlotInfo *slot;
d873dd1
+    CK_SLOT_ID slot_id, *key;
d873dd1
+    gint slot_series, token_slot_series;
d873dd1
+    ScSecurityToken *token;
d873dd1
+    GError *processing_error;
d873dd1
+
d873dd1
+    sc_debug ("waiting for token event");
d873dd1
+
d873dd1
+    /* FIXME: we return FALSE quite a bit in this function without cleaning up
d873dd1
+     * resources.  By returning FALSE we're going to ultimately exit anyway, but
d873dd1
+     * we should still be tidier about things.
d873dd1
+     */
d873dd1
+
d873dd1
+    slot = SECMOD_WaitForAnyTokenEvent (worker->module, 0, PR_SecondsToInterval (1)); 
d873dd1
+    processing_error = NULL;
d873dd1
+
d873dd1
+    if (slot == NULL) {
d873dd1
+	    int error_code;
d873dd1
+
d873dd1
+	    error_code = PORT_GetError ();
d873dd1
+	    if ((error_code == 0) || (error_code == SEC_ERROR_NO_EVENT)) {
d873dd1
+		    sc_debug ("spurrious event occurred");
d873dd1
+		    return TRUE;
d873dd1
+	    }
d873dd1
+
d873dd1
+	    /* FIXME: is there a function to convert from a PORT error
d873dd1
+	     * code to a translated string?
d873dd1
+	     */
d873dd1
+	    g_set_error (error, SC_SECURITY_TOKEN_MONITOR_ERROR,
d873dd1
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
d873dd1
+			 _("encountered unexpected error while "
d873dd1
+			   "waiting for security token events"));
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    /* the slot id and series together uniquely identify a token.
d873dd1
+     * You can never have two tokens with the same slot id at the
d873dd1
+     * same time, however (I think), so we can key off of it.
d873dd1
+     */
d873dd1
+    slot_id = PK11_GetSlotID (slot);
d873dd1
+    slot_series = PK11_GetSlotSeries (slot);
d873dd1
+
d873dd1
+    /* First check to see if there is a token that we're currently
d873dd1
+     * tracking in the slot.
d873dd1
+     */
d873dd1
+    key = g_new (CK_SLOT_ID, 1);
d873dd1
+    *key = slot_id;
d873dd1
+    token = g_hash_table_lookup (worker->security_tokens, key);
d873dd1
+
d873dd1
+    if (token != NULL)
d873dd1
+	    token_slot_series = sc_security_token_get_slot_series (token);
d873dd1
+
d873dd1
+    if (PK11_IsPresent (slot)) {
d873dd1
+	    /* Now, check to see if their is a new token in the slot.
d873dd1
+	     * If there was a different token in the slot now than
d873dd1
+	     * there was before, then we need to emit a removed signal
d873dd1
+	     * for the old token (we don't want unpaired insertion events).
d873dd1
+	     */
d873dd1
+	    if ((token != NULL) && 
d873dd1
+		token_slot_series != slot_series) {
d873dd1
+		    if (!sc_security_token_monitor_worker_emit_security_token_removed (worker, token, &processing_error)) {
d873dd1
+			    g_propagate_error (error, processing_error);
d873dd1
+			    return FALSE;
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+
d873dd1
+	    token = _sc_security_token_new (worker->module, 
d873dd1
+					    slot_id, slot_series);
d873dd1
+
d873dd1
+	    g_hash_table_replace (worker->security_tokens,
d873dd1
+				  key, token);
d873dd1
+	    key = NULL;
d873dd1
+
d873dd1
+	    if (!sc_security_token_monitor_worker_emit_security_token_inserted (worker, token, &processing_error)) {
d873dd1
+		    g_propagate_error (error, processing_error);
d873dd1
+		    return FALSE;
d873dd1
+	    }
d873dd1
+    } else {
d873dd1
+	    /* if we aren't tracking the token, just discard the event.
d873dd1
+	     * We don't want unpaired remove events.  Note on startup
d873dd1
+	     * NSS will generate an "insertion" event if a token is
d873dd1
+	     * already inserted in the slot.
d873dd1
+	     */
d873dd1
+	    if ((token != NULL)) {
d873dd1
+		    /* FIXME: i'm not sure about this code.  Maybe we
d873dd1
+		     * shouldn't do this at all, or maybe we should do it
d873dd1
+		     * n times (where n = slot_series - token_slot_series + 1)
d873dd1
+		     * 
d873dd1
+		     * Right now, i'm just doing it once.  
d873dd1
+		     */
d873dd1
+		    if ((slot_series - token_slot_series) > 1) {
d873dd1
+
d873dd1
+			    if (!sc_security_token_monitor_worker_emit_security_token_removed (worker, token, &processing_error)) {
d873dd1
+				    g_propagate_error (error, processing_error);
d873dd1
+				    return FALSE;
d873dd1
+			    }
d873dd1
+			    g_hash_table_remove (worker->security_tokens, key);
d873dd1
+
d873dd1
+			    token = _sc_security_token_new (worker->module, 
d873dd1
+							    slot_id, slot_series);
d873dd1
+			    g_hash_table_replace (worker->security_tokens,
d873dd1
+						  key, token);
d873dd1
+			    key = NULL;
d873dd1
+			    if (!sc_security_token_monitor_worker_emit_security_token_inserted (worker, token, &processing_error)) {
d873dd1
+				    g_propagate_error (error, processing_error);
d873dd1
+				    return FALSE;
d873dd1
+			    }
d873dd1
+		    }
d873dd1
+
d873dd1
+		    if (!sc_security_token_monitor_worker_emit_security_token_removed (worker, token, &processing_error)) {
d873dd1
+			    g_propagate_error (error, processing_error);
d873dd1
+			    return FALSE;
d873dd1
+		    }
d873dd1
+
d873dd1
+		    g_hash_table_remove (worker->security_tokens, key);
d873dd1
+		    token = NULL;
d873dd1
+	    } else {
d873dd1
+		    sc_debug ("got spurious remove event");
d873dd1
+	    }
d873dd1
+    }
d873dd1
+
d873dd1
+    g_free (key);
d873dd1
+    PK11_FreeSlot (slot);
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_monitor_create_worker (ScSecurityTokenMonitor *monitor,
d873dd1
+					 gint *worker_fd, GPid *worker_pid)
d873dd1
+{
d873dd1
+    GPid child_pid;
d873dd1
+    gint write_fd, read_fd;
d873dd1
+
d873dd1
+    write_fd = -1;
d873dd1
+    read_fd = -1;
d873dd1
+    if (!sc_open_pipe (&write_fd, &read_fd))
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    child_pid = sc_fork_and_disown ();
d873dd1
+
d873dd1
+    if (child_pid < 0)
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    if (child_pid == 0) {
d873dd1
+	    GError *error;
d873dd1
+	    ScSecurityTokenMonitorWorker *worker;
d873dd1
+
d873dd1
+/* FIXME: Gotta figure out why this isn't working
d873dd1
+*/
d873dd1
+#ifdef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
d873dd1
+	    gint fds_to_keep_open[] = { -1, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, -1 };
d873dd1
+
d873dd1
+	    SECMOD_DestroyModule (monitor->priv->module);
d873dd1
+	    monitor->priv->module = NULL;
d873dd1
+
d873dd1
+	    NSS_Shutdown ();
d873dd1
+
d873dd1
+	    fds_to_keep_open[0] = write_fd;
d873dd1
+	    sc_close_open_fds (fds_to_keep_open);
d873dd1
+	    read_fd = -1;
d873dd1
+
d873dd1
+	    if (!sc_load_nss (&error)) {
d873dd1
+		    sc_debug ("could not load nss - %s", error->message);
d873dd1
+		    g_error_free (error);
d873dd1
+		    _exit (1);
d873dd1
+	    }
d873dd1
+#else
d873dd1
+	    g_array_append_val (monitor->priv->fds_to_close_on_fork, read_fd);
d873dd1
+	    /* Junky workaround to keep from leaking fds
d873dd1
+	     */
d873dd1
+	    sc_close_fds ((gint *) monitor->priv->fds_to_close_on_fork->data,
d873dd1
+			  monitor->priv->fds_to_close_on_fork->len);
d873dd1
+#endif
d873dd1
+	    error = NULL;
d873dd1
+
d873dd1
+	    worker = sc_security_token_monitor_worker_new (write_fd);
d873dd1
+
d873dd1
+	    sc_security_token_monitor_worker_die_with_parent (worker);
d873dd1
+
d873dd1
+	    worker->module = sc_load_driver (monitor->priv->module_path, &error);
d873dd1
+
d873dd1
+	    if (worker->module == NULL) {
d873dd1
+		    sc_debug ("could not load nss driver - %s", error->message);
d873dd1
+		    g_error_free (error);
d873dd1
+		    _exit (2);
d873dd1
+	    }
d873dd1
+
d873dd1
+	    while (sc_security_token_monitor_worker_watch_for_and_process_event (worker, &error));
d873dd1
+
d873dd1
+	    sc_debug ("could not process token event - %s", error->message);
d873dd1
+	    sc_security_token_monitor_worker_free (worker);
d873dd1
+
d873dd1
+	    _exit (0);
d873dd1
+    }
d873dd1
+
d873dd1
+    close (write_fd);
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
d873dd1
+    g_array_append_val (monitor->priv->fds_to_close_on_fork, read_fd);
d873dd1
+#endif
d873dd1
+
d873dd1
+    if (worker_pid)
d873dd1
+	    *worker_pid = child_pid;
d873dd1
+
d873dd1
+    if (worker_fd)
d873dd1
+	    *worker_fd = read_fd;
d873dd1
+
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+#ifdef SC_SECURITY_TOKEN_MONITOR_ENABLE_TEST
d873dd1
+#include <glib.h>
d873dd1
+
d873dd1
+static GMainLoop *event_loop;
d873dd1
+static gboolean should_exit_on_next_remove = FALSE;
d873dd1
+
d873dd1
+static gboolean 
d873dd1
+on_timeout (ScSecurityTokenMonitor *monitor)
d873dd1
+{
d873dd1
+    GError *error;
d873dd1
+    g_print ("Re-enabling monitor.\n");
d873dd1
+
d873dd1
+    if (!sc_security_token_monitor_start (monitor, &error)) {
d873dd1
+	    g_warning ("could not start security token monitor - %s",
d873dd1
+		       error->message);
d873dd1
+	    g_error_free (error);
d873dd1
+	    return 1;
d873dd1
+    }
d873dd1
+    g_print ("Please re-insert security token\n");
d873dd1
+
d873dd1
+    should_exit_on_next_remove = TRUE;
d873dd1
+
d873dd1
+    return FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+on_device_inserted (ScSecurityTokenMonitor * monitor,
d873dd1
+		    ScSecurityToken *token)
d873dd1
+{
d873dd1
+    g_print ("security token inserted!\n");
d873dd1
+    g_print ("Please remove it.\n");
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+on_device_removed (ScSecurityTokenMonitor * monitor,
d873dd1
+		   ScSecurityToken *token)
d873dd1
+{
d873dd1
+    g_print ("security token removed!\n");
d873dd1
+
d873dd1
+    if (should_exit_on_next_remove)
d873dd1
+	    g_main_loop_quit (event_loop);
d873dd1
+    else {
d873dd1
+	    g_print ("disabling monitor for 2 seconds\n");
d873dd1
+	    sc_security_token_monitor_stop (monitor);
d873dd1
+	    g_timeout_add (2000, (GSourceFunc) on_timeout, monitor);
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+int 
d873dd1
+main (int   argc, 
d873dd1
+      char *argv[])
d873dd1
+{
d873dd1
+    ScSecurityTokenMonitor *monitor;
d873dd1
+    GError *error;
d873dd1
+
d873dd1
+    g_log_set_always_fatal (G_LOG_LEVEL_ERROR
d873dd1
+			    | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
d873dd1
+
d873dd1
+    g_type_init ();
d873dd1
+
d873dd1
+    g_message ("creating instance of 'security token monitor' object...");
d873dd1
+    monitor = sc_security_token_monitor_new (NULL);
d873dd1
+    g_message ("'security token monitor' object created successfully");
d873dd1
+
d873dd1
+    g_signal_connect (monitor, "security-token-inserted",
d873dd1
+		      G_CALLBACK (on_device_inserted), NULL);
d873dd1
+
d873dd1
+    g_signal_connect (monitor, "security-token-removed",
d873dd1
+		      G_CALLBACK (on_device_removed), NULL);
d873dd1
+
d873dd1
+    g_message ("starting listener...");
d873dd1
+
d873dd1
+    error = NULL;
d873dd1
+    if (!sc_security_token_monitor_start (monitor, &error)) {
d873dd1
+	    g_warning ("could not start security token monitor - %s",
d873dd1
+		       error->message);
d873dd1
+	    g_error_free (error);
d873dd1
+	    return 1;
d873dd1
+    }
d873dd1
+
d873dd1
+    event_loop = g_main_loop_new (NULL, FALSE);
d873dd1
+    g_main_loop_run (event_loop);
d873dd1
+    g_main_loop_unref (event_loop);
d873dd1
+    event_loop = NULL;
d873dd1
+
d873dd1
+    g_message ("destroying previously created 'security token monitor' object...");
d873dd1
+    g_object_unref (monitor);
d873dd1
+    monitor = NULL;
d873dd1
+    g_message ("'security token monitor' object destroyed successfully");
d873dd1
+
d873dd1
+    return 0;
d873dd1
+}
d873dd1
+#endif
d873dd1
--- /dev/null	2007-03-12 21:48:12.974260066 -0400
d873dd1
+++ gnome-screensaver-2.18.0/src/securitytoken.c	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -0,0 +1,680 @@
d873dd1
+/* securitytoken.c - security token
d873dd1
+ * 
d873dd1
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
d873dd1
+ * 
d873dd1
+ * This program is free software; you can redistribute it and/or modify
d873dd1
+ * it under the terms of the GNU General Public License as published by
d873dd1
+ * the Free Software Foundation; either version 2, or (at your option)
d873dd1
+ * any later version.
d873dd1
+ * 
d873dd1
+ * This program is distributed in the hope that it will be useful,
d873dd1
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
d873dd1
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d873dd1
+ * GNU General Public License for more details.
d873dd1
+ * 
d873dd1
+ * You should have received a copy of the GNU General Public License
d873dd1
+ * along with this program; if not, write to the Free Software
d873dd1
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
d873dd1
+ * 02111-1307, USA.  
d873dd1
+ *
d873dd1
+ * TODO:     - doing this per project is a bad idea i think.
d873dd1
+ *             We should probably make this a system service 
d873dd1
+ *             and use dbus.
d873dd1
+ *
d873dd1
+ *           - We hardcode a driver right now.  We should probably
d873dd1
+ *             look up the default list and go from there. 
d873dd1
+ */
d873dd1
+#define SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
d873dd1
+#include "securitytoken.h"
d873dd1
+
d873dd1
+#include <errno.h>
d873dd1
+#include <string.h>
d873dd1
+#include <unistd.h>
d873dd1
+
d873dd1
+#include <glib.h>
d873dd1
+#include <glib/gi18n.h>
d873dd1
+
d873dd1
+#include <cert.h>
d873dd1
+#include <nss.h>
d873dd1
+#include <pk11func.h>
d873dd1
+#include <prerror.h>
d873dd1
+#include <secmod.h>
d873dd1
+#include <secerr.h>
d873dd1
+
d873dd1
+#if defined (SC_SECURITY_TOKEN_ENABLE_TEST) || defined (SC_SECURITY_TOKEN_MONITOR_ENABLE_TEST)
d873dd1
+#define sc_debug(format, args...) g_printerr (format "\n", ##args)
d873dd1
+#define sc_warning(format, args...) g_printerr (format "\n", ##args)
d873dd1
+#else
d873dd1
+#define sc_debug(format, args...) 
d873dd1
+#define sc_warning(format, args...) 
d873dd1
+#endif
d873dd1
+
d873dd1
+struct _ScSecurityTokenPrivate {
d873dd1
+	SECMODModule *module;
d873dd1
+	ScSecurityTokenState state;
d873dd1
+
d873dd1
+	CK_SLOT_ID slot_id;
d873dd1
+	gint slot_series;
d873dd1
+
d873dd1
+	PK11SlotInfo *slot;
d873dd1
+	gchar *name;
d873dd1
+
d873dd1
+	CERTCertificate *signing_certificate;
d873dd1
+	CERTCertificate *encryption_certificate;
d873dd1
+};
d873dd1
+
d873dd1
+static void sc_security_token_finalize (GObject *object);
d873dd1
+static void sc_security_token_class_install_signals (ScSecurityTokenClass *token_class);
d873dd1
+static void sc_security_token_class_install_properties (ScSecurityTokenClass *token_class);
d873dd1
+static void sc_security_token_set_property (GObject       *object,
d873dd1
+					    guint          prop_id,
d873dd1
+					    const GValue  *value,
d873dd1
+					    GParamSpec    *pspec);
d873dd1
+static void sc_security_token_get_property (GObject     *object,
d873dd1
+					    guint        prop_id,
d873dd1
+					    GValue      *value,
d873dd1
+					    GParamSpec  *pspec);
d873dd1
+static void sc_security_token_set_name (ScSecurityToken *token, const gchar *name);
d873dd1
+static void sc_security_token_set_slot_id (ScSecurityToken *token, 
d873dd1
+					   gint             slot_id);
d873dd1
+static void sc_security_token_set_slot_series (ScSecurityToken *token, 
d873dd1
+					       gint             slot_series);
d873dd1
+static void sc_security_token_set_module (ScSecurityToken *token,
d873dd1
+					  SECMODModule    *module);
d873dd1
+
d873dd1
+static PK11SlotInfo *sc_security_token_find_slot_from_id (ScSecurityToken *token,
d873dd1
+							  gint slot_id);
d873dd1
+
d873dd1
+static PK11SlotInfo *sc_security_token_find_slot_from_token_name (ScSecurityToken *token,
d873dd1
+								  const gchar     *token_name);
d873dd1
+static gboolean sc_security_token_fetch_certificates (ScSecurityToken *token);
d873dd1
+
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_DEFAULT_SLOT_ID 
d873dd1
+#define SC_SECURITY_TOKEN_DEFAULT_SLOT_ID ((gulong) -1)
d873dd1
+#endif
d873dd1
+
d873dd1
+#ifndef SC_SECURITY_TOKEN_DEFAULT_SLOT_SERIES
d873dd1
+#define SC_SECURITY_TOKEN_DEFAULT_SLOT_SERIES -1
d873dd1
+#endif
d873dd1
+
d873dd1
+enum {
d873dd1
+	PROP_0 = 0,
d873dd1
+	PROP_NAME,
d873dd1
+	PROP_SLOT_ID,
d873dd1
+	PROP_SLOT_SERIES,
d873dd1
+	PROP_MODULE,
d873dd1
+	NUMBER_OF_PROPERTIES
d873dd1
+};
d873dd1
+
d873dd1
+enum {
d873dd1
+	INSERTED,
d873dd1
+	REMOVED,
d873dd1
+	NUMBER_OF_SIGNALS
d873dd1
+};
d873dd1
+
d873dd1
+static guint sc_security_token_signals[NUMBER_OF_SIGNALS];
d873dd1
+
d873dd1
+G_DEFINE_TYPE (ScSecurityToken, sc_security_token, G_TYPE_OBJECT);
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_class_init (ScSecurityTokenClass *token_class)
d873dd1
+{
d873dd1
+    GObjectClass *gobject_class;
d873dd1
+
d873dd1
+    gobject_class = G_OBJECT_CLASS (token_class);
d873dd1
+
d873dd1
+    gobject_class->finalize = sc_security_token_finalize;
d873dd1
+
d873dd1
+    sc_security_token_class_install_signals (token_class);
d873dd1
+    sc_security_token_class_install_properties (token_class);
d873dd1
+
d873dd1
+    g_type_class_add_private (token_class,
d873dd1
+			      sizeof (ScSecurityTokenPrivate));
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_class_install_signals (ScSecurityTokenClass *token_class)
d873dd1
+{
d873dd1
+    GObjectClass *object_class;
d873dd1
+
d873dd1
+    object_class = G_OBJECT_CLASS (token_class);
d873dd1
+
d873dd1
+    sc_security_token_signals[INSERTED] =
d873dd1
+	    g_signal_new ("inserted",
d873dd1
+			  G_OBJECT_CLASS_TYPE (object_class),
d873dd1
+			  G_SIGNAL_RUN_LAST,
d873dd1
+			  G_STRUCT_OFFSET (ScSecurityTokenClass,
d873dd1
+					   inserted), 
d873dd1
+			  NULL, NULL, g_cclosure_marshal_VOID__VOID, 
d873dd1
+			  G_TYPE_NONE, 0);
d873dd1
+    token_class->inserted = NULL;
d873dd1
+
d873dd1
+    sc_security_token_signals[REMOVED] =
d873dd1
+	    g_signal_new ("removed",
d873dd1
+			  G_OBJECT_CLASS_TYPE (object_class),
d873dd1
+			  G_SIGNAL_RUN_LAST,
d873dd1
+			  G_STRUCT_OFFSET (ScSecurityTokenClass,
d873dd1
+					   removed), 
d873dd1
+			  NULL, NULL, g_cclosure_marshal_VOID__VOID, 
d873dd1
+			  G_TYPE_NONE, 0);
d873dd1
+    token_class->removed = NULL;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_class_install_properties (ScSecurityTokenClass *token_class)
d873dd1
+{
d873dd1
+    GObjectClass *object_class;
d873dd1
+    GParamSpec *param_spec;
d873dd1
+
d873dd1
+    object_class = G_OBJECT_CLASS (token_class);
d873dd1
+    object_class->set_property = sc_security_token_set_property;
d873dd1
+    object_class->get_property = sc_security_token_get_property;
d873dd1
+
d873dd1
+    param_spec = g_param_spec_ulong ("slot-id", _("Slot ID"),
d873dd1
+				   _("The slot the token is in"),
d873dd1
+				   1, G_MAXULONG,
d873dd1
+				   SC_SECURITY_TOKEN_DEFAULT_SLOT_ID,
d873dd1
+				   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
d873dd1
+    g_object_class_install_property (object_class, PROP_SLOT_ID, param_spec);
d873dd1
+
d873dd1
+    param_spec = g_param_spec_int ("slot-series", _("Slot Series"),
d873dd1
+				   _("per-slot token identifier"),
d873dd1
+				   -1, G_MAXINT,
d873dd1
+				   SC_SECURITY_TOKEN_DEFAULT_SLOT_SERIES,
d873dd1
+				   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
d873dd1
+    g_object_class_install_property (object_class, PROP_SLOT_SERIES, param_spec);
d873dd1
+
d873dd1
+    param_spec = g_param_spec_string ("name", _("name"),
d873dd1
+				      _("name"), NULL,
d873dd1
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
d873dd1
+    g_object_class_install_property (object_class, PROP_NAME, param_spec);
d873dd1
+
d873dd1
+    param_spec = g_param_spec_pointer ("module", _("Module"),
d873dd1
+				       _("security token driver"),
d873dd1
+				       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
d873dd1
+    g_object_class_install_property (object_class, PROP_MODULE, param_spec);
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_set_property (GObject       *object,
d873dd1
+				guint          prop_id,
d873dd1
+				const GValue  *value,
d873dd1
+				GParamSpec    *pspec)
d873dd1
+{
d873dd1
+    ScSecurityToken *token = SC_SECURITY_TOKEN (object);
d873dd1
+
d873dd1
+    switch (prop_id)
d873dd1
+    {
d873dd1
+	    case PROP_NAME:
d873dd1
+		    sc_security_token_set_name (token, g_value_get_string (value));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    case PROP_SLOT_ID:
d873dd1
+		    sc_security_token_set_slot_id (token, 
d873dd1
+						   g_value_get_ulong (value));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    case PROP_SLOT_SERIES:
d873dd1
+		    sc_security_token_set_slot_series (token, 
d873dd1
+						       g_value_get_int (value));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    case PROP_MODULE:
d873dd1
+		    sc_security_token_set_module (token, 
d873dd1
+						  (SECMODModule *)
d873dd1
+						  g_value_get_pointer (value));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    default:
d873dd1
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+CK_SLOT_ID
d873dd1
+sc_security_token_get_slot_id (ScSecurityToken *token)
d873dd1
+{
d873dd1
+    return token->priv->slot_id;
d873dd1
+}
d873dd1
+
d873dd1
+ScSecurityTokenState 
d873dd1
+sc_security_token_get_state (ScSecurityToken *token)
d873dd1
+{
d873dd1
+    return token->priv->state;
d873dd1
+}
d873dd1
+
d873dd1
+gchar *
d873dd1
+sc_security_token_get_name (ScSecurityToken *token)
d873dd1
+{
d873dd1
+    return g_strdup (token->priv->name);
d873dd1
+}
d873dd1
+
d873dd1
+gboolean
d873dd1
+sc_security_token_is_login_token (ScSecurityToken *token)
d873dd1
+{
d873dd1
+    const gchar *login_token_name;
d873dd1
+    login_token_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME");
d873dd1
+
d873dd1
+    if ((login_token_name == NULL) || (token->priv->name == NULL))
d873dd1
+	    return FALSE;
d873dd1
+
d873dd1
+    if (strcmp (token->priv->name, login_token_name) == 0)
d873dd1
+	    return TRUE;
d873dd1
+
d873dd1
+    return FALSE;
d873dd1
+}
d873dd1
+
d873dd1
+static void 
d873dd1
+sc_security_token_get_property (GObject    *object,
d873dd1
+				guint        prop_id,
d873dd1
+				GValue      *value,
d873dd1
+				GParamSpec  *pspec)
d873dd1
+{
d873dd1
+    ScSecurityToken *token = SC_SECURITY_TOKEN (object);
d873dd1
+
d873dd1
+    switch (prop_id)
d873dd1
+    {
d873dd1
+	    case PROP_NAME:
d873dd1
+		    g_value_take_string (value, 
d873dd1
+					 sc_security_token_get_name (token));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    case PROP_SLOT_ID:
d873dd1
+		    g_value_set_ulong (value, 
d873dd1
+				       (gulong) sc_security_token_get_slot_id (token));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    case PROP_SLOT_SERIES:
d873dd1
+		    g_value_set_int (value, 
d873dd1
+				     sc_security_token_get_slot_series (token));
d873dd1
+		    break;
d873dd1
+
d873dd1
+	    default:
d873dd1
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_set_name (ScSecurityToken *token,
d873dd1
+			    const gchar     *name)
d873dd1
+{
d873dd1
+    if (name == NULL)
d873dd1
+	    return;
d873dd1
+
d873dd1
+    if ((token->priv->name == NULL) ||
d873dd1
+	(strcmp (token->priv->name, name) != 0)) {
d873dd1
+	    g_free (token->priv->name);
d873dd1
+	    token->priv->name = g_strdup (name);
d873dd1
+
d873dd1
+	    if (token->priv->slot == NULL) {
d873dd1
+		    token->priv->slot = sc_security_token_find_slot_from_token_name (token,
d873dd1
+										     token->priv->name);
d873dd1
+
d873dd1
+		    if (token->priv->slot != NULL) {
d873dd1
+			    gint slot_id, slot_series;
d873dd1
+
d873dd1
+			    slot_id = PK11_GetSlotID (token->priv->slot);
d873dd1
+			    if (slot_id != token->priv->slot_id)
d873dd1
+				    sc_security_token_set_slot_id (token, slot_id);
d873dd1
+
d873dd1
+			    slot_series = PK11_GetSlotSeries (token->priv->slot);
d873dd1
+			    if (slot_series != token->priv->slot_series)
d873dd1
+				    sc_security_token_set_slot_series (token, slot_series);
d873dd1
+
d873dd1
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_INSERTED);
d873dd1
+		    } else {
d873dd1
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_REMOVED);
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+
d873dd1
+
d873dd1
+	    g_object_notify (G_OBJECT (token), "name");
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_set_slot_id (ScSecurityToken *token,
d873dd1
+			       gint             slot_id)
d873dd1
+{
d873dd1
+    if (token->priv->slot_id != slot_id)
d873dd1
+    {
d873dd1
+	    token->priv->slot_id = slot_id;
d873dd1
+
d873dd1
+	    if (token->priv->slot == NULL) {
d873dd1
+		    token->priv->slot = sc_security_token_find_slot_from_id (token,
d873dd1
+									     token->priv->slot_id);
d873dd1
+
d873dd1
+		    if (token->priv->slot != NULL) {
d873dd1
+			    const gchar *token_name;
d873dd1
+
d873dd1
+			    token_name = PK11_GetTokenName (token->priv->slot);
d873dd1
+			    if ((token->priv->name == NULL) || 
d873dd1
+				((token_name != NULL) &&
d873dd1
+				(strcmp (token_name, token->priv->name) != 0)))
d873dd1
+				    sc_security_token_set_name (token, token_name);
d873dd1
+
d873dd1
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_INSERTED);
d873dd1
+		    } else {
d873dd1
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_REMOVED);
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+
d873dd1
+	    g_object_notify (G_OBJECT (token), "slot-id");
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_set_slot_series (ScSecurityToken *token,
d873dd1
+				   gint             slot_series)
d873dd1
+{
d873dd1
+    if (token->priv->slot_series != slot_series)
d873dd1
+    {
d873dd1
+	    token->priv->slot_series = slot_series;
d873dd1
+	    g_object_notify (G_OBJECT (token), "slot-series");
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_set_module (ScSecurityToken *token,
d873dd1
+			      SECMODModule    *module)
d873dd1
+{
d873dd1
+    gboolean should_notify;
d873dd1
+
d873dd1
+    if (token->priv->module != module)
d873dd1
+	    should_notify = TRUE;
d873dd1
+    else
d873dd1
+	    should_notify = FALSE;
d873dd1
+
d873dd1
+    if (token->priv->module != NULL) {
d873dd1
+	    SECMOD_DestroyModule (token->priv->module);
d873dd1
+	    token->priv->module = NULL;
d873dd1
+    }
d873dd1
+
d873dd1
+    if (module != NULL)
d873dd1
+	    token->priv->module = SECMOD_ReferenceModule (module);
d873dd1
+
d873dd1
+    if (should_notify)
d873dd1
+	    g_object_notify (G_OBJECT (token), "module");
d873dd1
+}
d873dd1
+
d873dd1
+gint 
d873dd1
+sc_security_token_get_slot_series (ScSecurityToken *token)
d873dd1
+{
d873dd1
+    return token->priv->slot_series;
d873dd1
+}
d873dd1
+
d873dd1
+static void
d873dd1
+sc_security_token_init (ScSecurityToken *token)
d873dd1
+{
d873dd1
+
d873dd1
+    sc_debug ("initializing security token ");
d873dd1
+
d873dd1
+    token->priv = G_TYPE_INSTANCE_GET_PRIVATE (token,
d873dd1
+					       SC_TYPE_SECURITY_TOKEN,
d873dd1
+					       ScSecurityTokenPrivate);
d873dd1
+
d873dd1
+    if (token->priv->slot != NULL)
d873dd1
+	    token->priv->name = g_strdup (PK11_GetTokenName (token->priv->slot));
d873dd1
+}
d873dd1
+
d873dd1
+static void sc_security_token_finalize (GObject *object)
d873dd1
+{
d873dd1
+    ScSecurityToken *token;
d873dd1
+    GObjectClass *gobject_class;
d873dd1
+
d873dd1
+    token = SC_SECURITY_TOKEN (object);
d873dd1
+
d873dd1
+    g_free (token->priv->name);
d873dd1
+
d873dd1
+    sc_security_token_set_module (token, NULL);
d873dd1
+
d873dd1
+    gobject_class =
d873dd1
+	    G_OBJECT_CLASS (sc_security_token_parent_class);
d873dd1
+
d873dd1
+    gobject_class->finalize (object);
d873dd1
+}
d873dd1
+
d873dd1
+GQuark sc_security_token_error_quark (void)
d873dd1
+{
d873dd1
+    static GQuark error_quark = 0;
d873dd1
+
d873dd1
+    if (error_quark == 0)
d873dd1
+	    error_quark = g_quark_from_static_string ("sc-security-token-error-quark");
d873dd1
+
d873dd1
+    return error_quark;
d873dd1
+}
d873dd1
+
d873dd1
+ScSecurityToken *
d873dd1
+_sc_security_token_new (SECMODModule *module,
d873dd1
+			CK_SLOT_ID    slot_id,
d873dd1
+			gint          slot_series)
d873dd1
+{
d873dd1
+    ScSecurityToken *token;
d873dd1
+
d873dd1
+    g_return_val_if_fail (module != NULL, NULL);
d873dd1
+    g_return_val_if_fail (slot_id >= 1, NULL);
d873dd1
+    g_return_val_if_fail (slot_series > 0, NULL);
d873dd1
+    g_return_val_if_fail (sizeof (gulong) == sizeof (slot_id), NULL);
d873dd1
+
d873dd1
+    token = SC_SECURITY_TOKEN (g_object_new (SC_TYPE_SECURITY_TOKEN,
d873dd1
+					     "module", module,
d873dd1
+					     "slot-id", (gulong) slot_id,
d873dd1
+					     "slot-series", slot_series,
d873dd1
+					     NULL));
d873dd1
+    return token;
d873dd1
+}
d873dd1
+
d873dd1
+ScSecurityToken *
d873dd1
+_sc_security_token_new_from_name (SECMODModule *module,
d873dd1
+				  const gchar  *name)
d873dd1
+{
d873dd1
+    ScSecurityToken *token;
d873dd1
+
d873dd1
+    g_return_val_if_fail (module != NULL, NULL);
d873dd1
+    g_return_val_if_fail (name != NULL, NULL);
d873dd1
+
d873dd1
+    token = SC_SECURITY_TOKEN (g_object_new (SC_TYPE_SECURITY_TOKEN,
d873dd1
+					     "module", module,
d873dd1
+					     "name", name,
d873dd1
+					     NULL));
d873dd1
+    return token;
d873dd1
+}
d873dd1
+
d873dd1
+void 
d873dd1
+_sc_security_token_set_state (ScSecurityToken      *token,
d873dd1
+			      ScSecurityTokenState  state)
d873dd1
+{
d873dd1
+    /* sc_security_token_fetch_certificates (token); */
d873dd1
+    if (token->priv->state != state)
d873dd1
+    {
d873dd1
+	    token->priv->state = state;
d873dd1
+
d873dd1
+	    if (state == SC_SECURITY_TOKEN_STATE_INSERTED) {
d873dd1
+		    g_signal_emit (token, sc_security_token_signals[INSERTED], 0);
d873dd1
+	    } else if (state == SC_SECURITY_TOKEN_STATE_REMOVED)
d873dd1
+		    g_signal_emit (token, sc_security_token_signals[REMOVED], 0);
d873dd1
+	    else
d873dd1
+		    g_assert_not_reached ();
d873dd1
+    }
d873dd1
+}
d873dd1
+
d873dd1
+/* So we could conceivably make the closure data a pointer to the token
d873dd1
+ * or something similiar and then emit signals when we want passwords,
d873dd1
+ * but it's probably easier to just get the password up front and use
d873dd1
+ * it.  So we just take the passed in g_malloc'd (well probably, who knows)
d873dd1
+ * and strdup it using NSPR's memory allocation routines.
d873dd1
+ */
d873dd1
+static char *
d873dd1
+sc_security_token_password_handler (PK11SlotInfo *slot, 
d873dd1
+				    PRBool        is_retrying, 
d873dd1
+				    const gchar  *password)
d873dd1
+{
d873dd1
+    if (is_retrying)
d873dd1
+	    return NULL;
d873dd1
+
d873dd1
+    return password != NULL? PL_strdup (password): NULL;
d873dd1
+}
d873dd1
+
d873dd1
+gboolean
d873dd1
+sc_security_token_unlock (ScSecurityToken *token,
d873dd1
+			  const gchar     *password)
d873dd1
+{   
d873dd1
+    SECStatus status;
d873dd1
+
d873dd1
+    PK11_SetPasswordFunc ((PK11PasswordFunc) sc_security_token_password_handler);
d873dd1
+
d873dd1
+    /* we pass PR_TRUE to load certificates
d873dd1
+     */
d873dd1
+    status = PK11_Authenticate (token->priv->slot, PR_TRUE, (gpointer) password);
d873dd1
+
d873dd1
+    if (status != SECSuccess) {
d873dd1
+	    sc_debug ("could not unlock token - %d", status);
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+static PK11SlotInfo *
d873dd1
+sc_security_token_find_slot_from_token_name (ScSecurityToken *token,
d873dd1
+					     const gchar     *token_name)
d873dd1
+{
d873dd1
+    int i;
d873dd1
+
d873dd1
+    for (i = 0; i < token->priv->module->slotCount; i++) {
d873dd1
+	    const gchar *slot_token_name;
d873dd1
+
d873dd1
+	    slot_token_name = PK11_GetTokenName (token->priv->module->slots[i]);
d873dd1
+
d873dd1
+	    if ((slot_token_name != NULL) &&
d873dd1
+		(strcmp (slot_token_name, token_name) == 0))
d873dd1
+		    return token->priv->module->slots[i];
d873dd1
+    }
d873dd1
+
d873dd1
+    return NULL;
d873dd1
+}
d873dd1
+
d873dd1
+static PK11SlotInfo *
d873dd1
+sc_security_token_find_slot_from_id (ScSecurityToken *token,
d873dd1
+                                     gint slot_id)
d873dd1
+{
d873dd1
+    int i;
d873dd1
+
d873dd1
+    for (i = 0; i < token->priv->module->slotCount; i++)
d873dd1
+	    if (PK11_GetSlotID (token->priv->module->slots[i]) == slot_id)
d873dd1
+		    return token->priv->module->slots[i];
d873dd1
+
d873dd1
+    return NULL;
d873dd1
+}
d873dd1
+
d873dd1
+static gboolean
d873dd1
+sc_security_token_fetch_certificates (ScSecurityToken *token)
d873dd1
+{
d873dd1
+    PK11SlotInfo *slot;
d873dd1
+    CERTCertList *certificates;
d873dd1
+    CERTCertListNode *node;
d873dd1
+    SECStatus status;
d873dd1
+    int i;
d873dd1
+
d873dd1
+    sc_security_token_unlock (token, "0000");
d873dd1
+
d873dd1
+    sc_debug ("fetching certificates for token in slot %lu",
d873dd1
+	      token->priv->slot_id);
d873dd1
+
d873dd1
+    slot = sc_security_token_find_slot_from_id (token,
d873dd1
+						token->priv->slot_id);
d873dd1
+
d873dd1
+    g_assert (PK11_GetSlotID (slot) == token->priv->slot_id);
d873dd1
+
d873dd1
+    if (i == token->priv->module->slotCount) {
d873dd1
+	    sc_debug ("could not find slot %lu", token->priv->slot_id);
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    certificates = PK11_ListCertsInSlot (slot);
d873dd1
+
d873dd1
+    sc_debug ("filtering out non-user certificates");
d873dd1
+    if (CERT_FilterCertListForUserCerts (certificates) != SECSuccess) {
d873dd1
+	    CERT_DestroyCertList (certificates);
d873dd1
+	    sc_debug ("could not filter out non-user certificates");
d873dd1
+	    return FALSE;
d873dd1
+    }
d873dd1
+
d873dd1
+    for (node = CERT_LIST_HEAD (certificates); 
d873dd1
+	 !CERT_LIST_END (node, certificates);
d873dd1
+	 node = CERT_LIST_NEXT(node)) {
d873dd1
+
d873dd1
+	    SECCertificateUsage cert_usages;
d873dd1
+
d873dd1
+	    sc_debug ("verifying certificate for use");
d873dd1
+	    status = CERT_VerifyCertificateNow (NULL, node->cert, TRUE, 
d873dd1
+						0, NULL, &cert_usages);
d873dd1
+
d873dd1
+	    if (status != SECSuccess) {
d873dd1
+		    sc_debug ("could not be verified, skipping...");
d873dd1
+		    continue;
d873dd1
+	    }
d873dd1
+
d873dd1
+	    sc_debug ("got cert with usages 0x%lx", (gulong) cert_usages);
d873dd1
+
d873dd1
+	    if (token->priv->encryption_certificate == NULL) {
d873dd1
+
d873dd1
+		    sc_debug ("checking if certificate can be used for data "
d873dd1
+			      "encryption");
d873dd1
+		    status = CERT_CheckCertUsage (node->cert, 
d873dd1
+						  KU_DATA_ENCIPHERMENT);
d873dd1
+
d873dd1
+		    if (status == SECSuccess) {
d873dd1
+			    token->priv->encryption_certificate = 
d873dd1
+				    CERT_DupCertificate (node->cert);
d873dd1
+		    } else {
d873dd1
+			    sc_debug ("certificate can not be used for encryption");
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+
d873dd1
+	    if (token->priv->signing_certificate == NULL) {
d873dd1
+
d873dd1
+		    sc_debug ("checking if certificate can be used for data "
d873dd1
+			      "signing");
d873dd1
+		    status = CERT_CheckCertUsage (node->cert, 
d873dd1
+						  KU_DIGITAL_SIGNATURE);
d873dd1
+
d873dd1
+		    if (status == SECSuccess) {
d873dd1
+			    token->priv->signing_certificate = 
d873dd1
+				    CERT_DupCertificate (node->cert);
d873dd1
+		    } else {
d873dd1
+			    sc_debug ("certificate can not be used for signing things");
d873dd1
+		    }
d873dd1
+	    }
d873dd1
+    }
d873dd1
+    return TRUE;
d873dd1
+}
d873dd1
+
d873dd1
+#ifdef SC_SECURITY_TOKEN_ENABLE_TEST
d873dd1
+#include <glib.h>
d873dd1
+
d873dd1
+static GMainLoop *event_loop;
d873dd1
+
d873dd1
+int 
d873dd1
+main (int   argc, 
d873dd1
+      char *argv[])
d873dd1
+{
d873dd1
+    ScSecurityToken *token;
d873dd1
+    GError *error;
d873dd1
+
d873dd1
+    g_log_set_always_fatal (G_LOG_LEVEL_ERROR
d873dd1
+			    | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
d873dd1
+
d873dd1
+    g_type_init ();
d873dd1
+
d873dd1
+    g_message ("creating instance of 'security token' object...");
d873dd1
+    token = _sc_security_token_new (NULL, 1, 1);
d873dd1
+    g_message ("'security token' object created successfully");
d873dd1
+
d873dd1
+    g_message ("destroying previously created 'security token' object...");
d873dd1
+    g_object_unref (token);
d873dd1
+    token = NULL;
d873dd1
+    g_message ("'security token' object destroyed successfully");
d873dd1
+
d873dd1
+    return 0;
d873dd1
+}
d873dd1
+#endif
d873dd1
--- gnome-screensaver-2.18.0/src/gs-window-x11.c.securitytoken	2007-02-22 23:39:09.000000000 -0500
d873dd1
+++ gnome-screensaver-2.18.0/src/gs-window-x11.c	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -806,6 +806,7 @@
d873dd1
                 "XAUTHLOCALHOSTNAME",
d873dd1
                 "KRB5CCNAME",
d873dd1
                 "KRBTKFILE",
d873dd1
+                "PKCS11_LOGIN_TOKEN_NAME",
d873dd1
                 "LANG",
d873dd1
                 "LANGUAGE",
d873dd1
                 "LC_ALL",
d873dd1
--- gnome-screensaver-2.18.0/src/Makefile.am.securitytoken	2007-03-12 13:07:47.000000000 -0400
d873dd1
+++ gnome-screensaver-2.18.0/src/Makefile.am	2007-03-14 10:56:44.000000000 -0400
d873dd1
@@ -50,6 +50,18 @@
d873dd1
 	test-window	\
d873dd1
 	$(NULL)
d873dd1
 
d873dd1
+noinst_LIBRARIES = libsecuritytoken.a
d873dd1
+
d873dd1
+libsecuritytoken_a_SOURCES = \
d873dd1
+	securitytoken.h \
d873dd1
+	securitytoken.c \
d873dd1
+	securitytokenmonitor.h \
d873dd1
+	securitytokenmonitor.c
d873dd1
+
d873dd1
+libsecuritytoken_a_CFLAGS = $(SECURITY_TOKEN_CFLAGS) \
d873dd1
+			    -DLIBDIR=\""$(libdir)"\"  \
d873dd1
+			    -DSYSCONFDIR=\""$(sysconfdir)"\"
d873dd1
+
d873dd1
 gnome_screensaver_command_SOURCES = 	\
d873dd1
 	gnome-screensaver-command.c
d873dd1
 
d873dd1
@@ -204,6 +216,7 @@
d873dd1
 gnome_screensaver_LDADD =		\
d873dd1
 	$(GNOME_SCREENSAVER_LIBS)	\
d873dd1
 	$(SAVER_LIBS)			\
d873dd1
+ 	$(top_builddir)/src/libsecuritytoken.a \
d873dd1
 	$(NULL)
d873dd1
 
d873dd1
 gnome_screensaver_LDFLAGS = -export-dynamic
d873dd1
--- gnome-screensaver-2.18.0/configure.ac.securitytoken	2007-03-01 13:16:35.000000000 -0500
d873dd1
+++ gnome-screensaver-2.18.0/configure.ac	2007-03-14 10:55:13.000000000 -0400
d873dd1
@@ -44,6 +44,7 @@
d873dd1
 LIBEXIF_REQUIRED_VERSION=0.6.12
d873dd1
 GLIB_REQUIRED_VERSION=2.7.0
d873dd1
 LIBGNOMEKBDUI_REQUIRED_VERSION=0.1
d873dd1
+NSS_REQUIRED_VERSION=3.11.2
d873dd1
 
d873dd1
 AC_CHECK_HEADERS(unistd.h)
d873dd1
 AC_CHECK_HEADERS(crypt.h sys/select.h)
d873dd1
@@ -57,7 +58,8 @@
d873dd1
         gtk+-2.0 >= $GTK_REQUIRED_VERSION
d873dd1
         dbus-glib-1 >= $DBUS_REQUIRED_VERSION
d873dd1
         gconf-2.0 >= $GCONF_REQUIRED_VERSION
d873dd1
-        libgnome-menu >= $LIBGNOME_MENU_REQUIRED_VERSION)
d873dd1
+        libgnome-menu >= $LIBGNOME_MENU_REQUIRED_VERSION
d873dd1
+	nss >= $NSS_REQUIRED_VERSION)
d873dd1
 AC_SUBST(GNOME_SCREENSAVER_CFLAGS)
d873dd1
 AC_SUBST(GNOME_SCREENSAVER_LIBS)
d873dd1
 
d873dd1
@@ -92,8 +94,6 @@
d873dd1
 
d873dd1
 AM_GCONF_SOURCE_2
d873dd1
 
d873dd1
-GNOME_COMPILE_WARNINGS(yes)
d873dd1
-
d873dd1
 # Solaris requires libresolv for daemon()
d873dd1
 case "$host" in
d873dd1
 	*-*-solaris*)
d873dd1
@@ -227,6 +227,16 @@
d873dd1
 AM_CONDITIONAL(HAVE_USER_SWITCHING, test x$enable_user_switching = xyes)
d873dd1
 AC_SUBST(HAVE_USER_SWITCHING)
d873dd1
 
d873dd1
+
d873dd1
+# security token support
d873dd1
+PKG_CHECK_MODULES(SECURITY_TOKEN,
d873dd1
+        gobject-2.0 >= $GLIB_REQUIRED_VERSION
d873dd1
+	nss >= $NSS_REQUIRED_VERSION)
d873dd1
+AC_SUBST(SECURITY_TOKEN_CFLAGS)
d873dd1
+AC_SUBST(SECURITY_TOKEN_LIBS)
d873dd1
+
d873dd1
+
d873dd1
+
d873dd1
 dnl ---------------------------------------------------------------------------
d873dd1
 dnl - Where should we put documentation ?
d873dd1
 dnl ---------------------------------------------------------------------------