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

Re: Shutdown from Gnome



Hi,

Am Mo, den 26.07.2004 schrieb Mattias Eriksson um 15:32:
> In gdm 2.6.x it is possible to do this by the use of the SUP protocol[1]
> but this is not implemented in the session-manager upstream. So instead
> of doing a ugly hack I suggest just implementing the use of SUP in
> gnome-session (or whatever is handling the logout these days).

It seems that while I wrote my initial message, jarno implemented that
in gnome-session and put it on his private repository:

deb http://jarno.gmxhome.de/deb-archive/ unstable/
deb-src http://jarno.gmxhome.de/deb-archive/ unstable/

For those interested in the details, I attached his patch.

Would it be possible to have that in the normal debian package soon?

Thx,
nomeata
-- 
Joachim "nomeata" Breitner
Debian Developer
  nomeata@debian.org | ICQ# 74513189 | GPG-Keyid: 4743206C
  JID: joachimbreitner@amessage.de | http://people.debian.org/~nomeata
diff -urNad gnome-session-2.6.2.halfpatched/gnome-session/Makefile.am gnome-session-2.6.2/gnome-session/Makefile.am
--- gnome-session-2.6.2.halfpatched/gnome-session/Makefile.am	2004-07-25 17:58:43.000000000 +0200
+++ gnome-session-2.6.2/gnome-session/Makefile.am	2004-07-21 22:45:01.000000000 +0200
@@ -64,6 +64,9 @@
 
 logout_test_SOURCES =		\
 	logout-test.c		\
+	logout.h		\
+	gdm-talk.c		\
+	gdm-talk.h		\
 	util.c			\
 	util.h			\
 	gsm-multiscreen.c	\
@@ -87,6 +90,8 @@
 	remote.h		\
 	logout.c		\
 	logout.h		\
+	gdm-talk.c		\
+	gdm-talk.h		\
 	splash-widget.c		\
 	splash-widget.h		\
 	gsm-xrandr.c		\
diff -urNad gnome-session-2.6.2.halfpatched/gnome-session/Makefile.in gnome-session-2.6.2/gnome-session/Makefile.in
--- gnome-session-2.6.2.halfpatched/gnome-session/Makefile.in	2004-07-25 17:58:43.000000000 +0200
+++ gnome-session-2.6.2/gnome-session/Makefile.in	2004-07-21 22:45:01.000000000 +0200
@@ -271,6 +271,8 @@
 
 logout_test_SOURCES = \
 	logout-test.c		\
+	gdm-talk.c		\
+	gdm-talk.h		\
 	util.c			\
 	util.h			\
 	gsm-multiscreen.c	\
@@ -295,6 +297,8 @@
 	remote.h		\
 	logout.c		\
 	logout.h		\
+	gdm-talk.c		\
+	gdm-talk.h		\
 	splash-widget.c		\
 	splash-widget.h		\
 	gsm-xrandr.c		\
@@ -425,7 +429,7 @@
 	egg-screen-url.$(OBJEXT)
 am_gnome_session_OBJECTS = manager.$(OBJEXT) ice.$(OBJEXT) \
 	main.$(OBJEXT) prop.$(OBJEXT) save.$(OBJEXT) command.$(OBJEXT) \
-	remote.$(OBJEXT) logout.$(OBJEXT) splash-widget.$(OBJEXT) \
+	remote.$(OBJEXT) logout.$(OBJEXT) gdm-talk.$(OBJEXT) splash-widget.$(OBJEXT) \
 	gsm-xrandr.$(OBJEXT) gsm-keyring.$(OBJEXT) gsm-gsd.$(OBJEXT) \
 	gsm-protocol.$(OBJEXT) gsm-sound.$(OBJEXT) \
 	gsm-at-startup.$(OBJEXT) gsm-multiscreen.$(OBJEXT) \
@@ -453,7 +457,7 @@
 gnome_session_save_OBJECTS = $(am_gnome_session_save_OBJECTS)
 gnome_session_save_DEPENDENCIES =
 gnome_session_save_LDFLAGS =
-am_logout_test_OBJECTS = logout-test.$(OBJEXT) util.$(OBJEXT) \
+am_logout_test_OBJECTS = logout-test.$(OBJEXT) gdm-talk.$(OBJEXT) util.$(OBJEXT) \
 	gsm-multiscreen.$(OBJEXT) $(am__objects_1)
 logout_test_OBJECTS = $(am_logout_test_OBJECTS)
 logout_test_DEPENDENCIES =
diff -urNad gnome-session-2.6.2.halfpatched/gnome-session/gdm-talk.c gnome-session-2.6.2/gnome-session/gdm-talk.c
--- gnome-session-2.6.2.halfpatched/gnome-session/gdm-talk.c	1970-01-01 01:00:00.000000000 +0100
+++ gnome-session-2.6.2/gnome-session/gdm-talk.c	2004-07-25 17:32:35.000000000 +0200
@@ -0,0 +1,1181 @@
+/* gdm-talk.c - Talk to GDM about session followup actions.
+
+   Written by Jarno Gassenbauer <jarno@gmx.net>
+   Copyright (C) Jarno Gassenbauer
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+#include <X11/Xauth.h>
+#include <X11/Xos.h>
+#include <stdio.h>
+#include <X11/Xlib.h>  	       /* for Family contants */
+#include <gdk/gdk.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include "gdm-talk.h" 
+
+
+#define MIT_COOKIE_BYTE_LENGTH 	       16
+
+#define GDM_AUTH_AND_COOKIE_RAW	       "AUTH_LOCAL %s"
+#define GDM_SOCKETNAME 		       "/tmp/.gdm_socket"
+#define GDMT_FBU_BUFFER_MAX_SIZE      5000
+
+/* from gdm's daemon/gdm.h */
+#define GDM_SUP_VERSION			"VERSION" /* no arguments */
+#define GDM_SUP_QUERY_LOGOUT_ACTION	"QUERY_LOGOUT_ACTION"
+#define GDM_SUP_SET_LOGOUT_ACTION	"SET_LOGOUT_ACTION" /* <action> */
+#define GDM_SUP_CLOSE			"CLOSE" /* None */
+
+#define GDM_SUP_LOGOUT_ACTION_NONE	"NONE"
+#define GDM_SUP_LOGOUT_ACTION_HALT	"HALT"
+#define GDM_SUP_LOGOUT_ACTION_REBOOT	"REBOOT"
+#define GDM_SUP_LOGOUT_ACTION_SUSPEND	"SUSPEND"
+
+/* shortcuts */
+#define GDM_LOGOUT_VERSION	  GDM_SUP_VERSION
+#define GDM_LOGOUT_QUERY	  GDM_SUP_QUERY_LOGOUT_ACTION
+#define GDM_LOGOUT_ACTION_NONE    GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_NONE
+#define GDM_LOGOUT_ACTION_HALT    GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_HALT
+#define GDM_LOGOUT_ACTION_REBOOT  GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_REBOOT
+#define GDM_LOGOUT_ACTION_SUSPEND GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_SUSPEND
+#define GDM_LOGOUT_CLOSE	  GDM_SUP_CLOSE
+
+#define IGNORE_EINTR(expr)	\
+  do {				\
+	  errno = 0;		\
+	  expr;			\
+  } while (errno == EINTR);
+
+GDMTActions gdmt_default_action;
+
+unsigned int gdmt_available_actions;
+
+
+G_BEGIN_DECLS
+
+
+struct _FlowBuffer
+{
+  gchar    *name;     /* for error reporting */
+  GString  *buff;     /* yay, unlike most girls, two FlowBuffers may */
+  gsize    max_size;  /*   share the same GString simultaneously */
+
+  gboolean	       (*refill) (FlowBuffer *fb);
+  gboolean	       (*drain) (gchar *line, gsize *len, FlowBuffer *fb);
+  gchar		       *drain_delimiter;
+
+  GDMTErrorLoggerFunc error_logger;
+  gpointer	       error_logger_data;
+
+  gpointer	       user_data;
+};
+
+
+struct _GDMTContext
+{
+  guint			ref_count;
+  GDMTErrorLoggerFunc	error_logger;
+  gpointer		error_logger_data;
+
+  GIOChannel		*channel;
+  gint			source_id;
+  GDMTChannelState	channel_state;
+  GIOCondition		cond_current;
+  GIOCondition		cond_next;
+
+   /* entries might only be removed upon acknowledge from GDM */
+  GQueue		*command_queue;
+  gchar*		send_strings[GDMT_GDMCMD_N];
+    
+  FlowBuffer		write_buffer;
+  FlowBuffer		read_buffer;
+
+  GDMTGotResponseFunc	got_response;
+  gpointer		got_response_user_data;
+};
+
+
+
+static const gchar* const gdmt_gdmcmd_to_string[GDMT_GDMCMD_N] = {
+      GDM_SUP_VERSION,
+      NULL,		       /* to be set up at copy time */
+      GDM_SUP_QUERY_LOGOUT_ACTION,
+      GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_NONE,
+      GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_HALT,
+      GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_REBOOT,
+      GDM_SUP_SET_LOGOUT_ACTION " " GDM_SUP_LOGOUT_ACTION_SUSPEND,
+      GDM_SUP_CLOSE,
+      NULL
+};
+
+const gchar* const gdmt_gdmcmd_to_debug[GDMT_GDMCMD_N] = {
+      "version",
+      "authenticate",
+      "query logout action",
+      "set logout action to none",
+      "set logout action to halt",
+      "set logout action to reboot",
+      "set logout action to suspend",
+      "close connection",
+      "<idle>"
+};
+
+static const GDMTChannelState gdmt_gdmcmd_prerequisite[GDMT_GDMCMD_N] = {
+      GDMT_CHANNL_OPEN,
+      GDMT_CHANNL_OPEN,
+      GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED,
+      GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED,
+      GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED,
+      GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED,
+      GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED,
+      0,	       /* no need to open a connection just to close it */
+      0
+};
+
+
+/*** Chapter 1: Startup, Shutdown */
+static gboolean		gdmt_socket_open (GDMTContext *ct, const gchar *socket_name);
+
+
+/*** Chapter 2: Low level Read/Write Management */
+static gboolean		gdmt_channel_update_watch (GDMTContext *ct);
+static void		gdmt_write_enable	  (GDMTContext *ct);
+static void		gdmt_read_enable 	  (GDMTContext *ct);
+static void		gdmt_write_disable	  (GDMTContext *ct);
+static void		gdmt_read_disable	  (GDMTContext *ct);
+static gboolean		gdmt_io_handler		  (GIOChannel *source, GIOCondition condition, gpointer data);
+static gboolean		gdmt_fbu_write_drain	  (gchar *line, gsize *len, FlowBuffer *fb);
+static gboolean		gdmt_fbu_write_refill	  (FlowBuffer *eb);
+static gboolean		gdmt_fbu_read_refill	  (FlowBuffer *fb);
+static gboolean		gdmt_fbu_read_drain	  (gchar *line, gsize *len, FlowBuffer *fb);
+
+
+/*** Chapter 3: High level Read/Write data processing */
+static gboolean		gdmt_gdm_response_handler (gchar *line, gsize length, GDMTContext *ct);
+static gint		gdmt_parse_gdm_response1  (gchar **buffer);
+static void		gdmt_queue_line		  (GDMTContext *context, const char* line);
+
+
+/*** Chapter 4: Command Queue */
+static void		gdmt_push_cmd		  (GDMTContext *ct, GDMTGdmCommand cmd);
+static void		gdmt_flush_cmd		  (GDMTContext *ct);
+static GDMTGdmCommand	gdmt_peek_cmd		  (GDMTContext *ct);
+static GDMTGdmCommand	gdmt_pop_cmd		  (GDMTContext *ct);
+gboolean		gdmt_is			  (GDMTContext *ct, GDMTChannelState flags);
+
+
+/*** Chapter 5: Authentication */
+static char *		get_mit_cookie		  (gchar *buffer, gint buffer_length);
+
+
+/*** Chapter 6: Logging functions (fallback) */
+static void		gdmt_message_critical	  (const gchar *format, ...) G_GNUC_PRINTF (1, 2);
+
+
+/*** Appendix A: String utility functions */
+static void		bin2hex			  (unsigned int binlen, const char *bindata, char *hexdata);
+static gchar*		ascii_strsep		  (gchar **string_ptr, const gchar *delimiters);
+
+
+/*** Appendix B: Flow Buffer functions */
+static gboolean		fbu_refill		  (FlowBuffer *fb);
+static gboolean		fbu_drain		  (FlowBuffer *fb);
+static gboolean		fbu_grow_buffer		  (FlowBuffer *fb, gsize min_headroom);
+
+G_END_DECLS
+
+
+
+
+/*** Chapter 1: Startup, Shutdown
+ *.............................................................
+ */
+
+GDMTContext*
+gdmt_startup ( GDMTErrorLoggerFunc err_func,
+	       GDMTGotResponseFunc handler,
+	       gpointer		   handler_data)
+{
+  GDMTContext *ct;
+  FlowBuffer *b;
+  char* retval;
+  gchar       cookie[2*MIT_COOKIE_BYTE_LENGTH + 1];
+
+  ct = g_new0 (GDMTContext, 1);
+
+  ct->ref_count = 1;
+  if (err_func == NULL)
+      err_func = (GDMTErrorLoggerFunc)&gdmt_message_critical;
+  ct->error_logger = err_func;
+
+  ct->command_queue = g_queue_new ();
+  retval = get_mit_cookie(cookie, 2*MIT_COOKIE_BYTE_LENGTH);
+  if (retval!=NULL) {
+      (*ct->error_logger)("Could not get authentication cookie for GDM: %s\n",
+			   retval);
+      g_free (retval);
+      goto spaghetti_abort;
+  }
+  cookie[2*MIT_COOKIE_BYTE_LENGTH] = '\0';
+  /* copy table of conversation lines for GDM */
+  memcpy (ct->send_strings, gdmt_gdmcmd_to_string, sizeof (ct->send_strings));
+  /* prepare the conversation line for authentication to contain the cookie */
+  ct->send_strings[GDMT_GDMCMD_AUTH] =
+      g_strdup_printf (GDM_AUTH_AND_COOKIE_RAW, cookie);
+
+
+  /* to send data to gdm */
+  b = &ct->write_buffer;
+  b->name		= g_strdup ("GDM-write-queue");
+  b->buff		= NULL;
+  b->max_size		= GDMT_FBU_BUFFER_MAX_SIZE;
+  b->refill		= &gdmt_fbu_write_refill;
+  b->drain		= &gdmt_fbu_write_drain;
+  b->drain_delimiter	= NULL;
+  b->error_logger	= err_func;
+  b->error_logger_data	= NULL;
+  b->user_data		= ct;
+
+  /* to handle data coming from gdm to us */
+  b = &ct->read_buffer;
+  b->name		= g_strdup ("GDM-read-queue");
+  b->buff		= NULL;
+  b->max_size		= GDMT_FBU_BUFFER_MAX_SIZE;
+  b->drain		= &gdmt_fbu_read_drain;
+  b->drain_delimiter	= "\n";
+  b->refill		= &gdmt_fbu_read_refill;
+  b->error_logger	= err_func;
+  b->error_logger_data	= "gdmt_io_read_handler, %s: %s\n";
+  b->user_data		= ct;
+
+  /* allow callback from now on since we cannot fail anymore */
+  ct->got_response = handler;
+  ct->got_response_user_data = handler_data;
+  return ct;
+
+spaghetti_abort:
+  ct->channel_state |= GDMT_CHANNL_SHUTDOWN;
+  gdmt_unref (ct);
+  return NULL;
+}
+
+
+gboolean
+gdmt_running  (GDMTContext *ct)
+{
+  return ! gdmt_is (ct, GDMT_CHANNL_SHUTDOWN);
+}
+
+
+void
+gdmt_shutdown (GDMTContext *context)
+{
+  GDMTGotResponseFunc racer;
+
+  g_assert (context != NULL);
+
+  gdmt_flush_cmd (context);
+  context->cond_next = 0;
+  context->channel_state |= GDMT_CHANNL_SHUTDOWN;
+  gdmt_channel_update_watch (context);
+
+  context->channel_state &= ~(GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED);
+  if (context->channel != NULL)
+      g_io_channel_shutdown (context->channel, FALSE, NULL);
+  context->channel = NULL;
+
+  racer = context->got_response;
+  if (racer != NULL) {
+      context->got_response = NULL; /* avoid recursion */
+      (*racer) (NULL, GDMT_GDMCMD_CLOSE, 0, context->got_response_user_data);
+  }
+}
+
+
+void
+gdmt_ref (GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+  ++ct->ref_count;
+}
+
+void
+gdmt_unref (GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+
+  if (--ct->ref_count > 0)
+      return;
+
+  gdmt_shutdown (ct);
+
+  if (ct->channel != NULL)
+      g_io_channel_unref (ct->channel); /* closes socket too */
+
+  g_free (ct->write_buffer.name);
+  g_free (ct-> read_buffer.name);
+  if (ct->write_buffer.buff != NULL)
+      g_string_free (ct->write_buffer.buff, TRUE);
+  if (ct-> read_buffer.buff != NULL)
+      g_string_free (ct-> read_buffer.buff, TRUE);
+  
+  g_free (ct->send_strings[GDMT_GDMCMD_AUTH]);
+
+  g_free (ct);
+}
+
+
+static gboolean
+gdmt_socket_open (GDMTContext *ct, const gchar *socket_name)
+{
+  struct sockaddr_un name;
+  int sock;
+  GString *s;
+  GQueue *q;
+  int retval;
+  struct stat stat_buf;
+
+  g_assert (ct != NULL);
+
+  q = ct->command_queue;
+  ct->command_queue = g_queue_new ();
+
+  /* shut down channel and flush buffers */
+  if (ct->channel != NULL) {
+      if (ct->cond_current)
+	       g_source_remove (ct->source_id);
+      ct->cond_current = ct->cond_next = 0;
+      g_io_channel_shutdown (ct->channel, FALSE, NULL);
+      ct->channel = NULL;
+      if ((s = ct->write_buffer.buff) != NULL)
+	       g_string_truncate (s, 0);
+      if ((s = ct->read_buffer.buff) != NULL)
+	       g_string_truncate (s, 0);
+  }
+
+  ct->channel_state &= ~(GDMT_CHANNL_OPEN | GDMT_CHANNL_AUTH7ED);
+
+  /* check ownership of GDM socket */
+  IGNORE_EINTR (retval = stat (socket_name, &stat_buf))
+  if (retval < 0 || stat_buf.st_uid != 0
+  		|| access (socket_name, R_OK|W_OK) != 0) {
+      (*ct->error_logger) ("Wrong file permissions on %s\n", socket_name);
+      g_queue_free (q);
+      ct->channel_state |= GDMT_CHANNL_SHUTDOWN;
+      return FALSE;
+  }
+
+  /* open socket */
+  IGNORE_EINTR (sock = socket (PF_LOCAL, SOCK_STREAM, 0))
+  if (sock < 0) {
+      (*ct->error_logger) ("Could not open socket for GDM: %s\n",
+			       g_strerror (errno));
+      g_queue_free (q);
+      ct->channel_state |= GDMT_CHANNL_SHUTDOWN;
+      return FALSE;
+  }
+      
+   /* connect socket */
+  name.sun_family = AF_LOCAL;
+  strncpy (name.sun_path, socket_name, sizeof (name.sun_path));
+  name.sun_path[sizeof (name.sun_path) - 1] = '\0';
+  /* can block with TCP; not sure if with UNIX domain sockets as well */
+  IGNORE_EINTR (retval =
+  	connect (sock, (struct sockaddr *) &name, SUN_LEN (&name)))
+  if (retval < 0) {
+      (*ct->error_logger)("Could not bind GDM socket to %s: %s\n",
+			       name.sun_path, g_strerror (errno));
+      g_queue_free (q);
+      ct->channel_state |= GDMT_CHANNL_SHUTDOWN;
+      return FALSE;
+  }
+  
+
+
+
+  /* set up channel */
+  ct->channel = g_io_channel_unix_new (sock);
+  g_io_channel_set_close_on_unref (ct->channel, TRUE);
+  g_io_channel_set_encoding (ct->channel, NULL, NULL);
+  g_io_channel_set_buffered (ct->channel, FALSE);
+  g_io_channel_set_flags (ct->channel, g_io_channel_get_flags (ct->channel)
+			 | G_IO_FLAG_NONBLOCK, NULL);
+
+  ct->channel_state |= GDMT_CHANNL_OPEN | GDMT_CHANNL_WRITEABLE
+		      | GDMT_CHANNL_READABLE;
+  ct->cond_next |= G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+  /* resend commands that we were waiting for an acknowledgement */
+  if (q == NULL) {
+      gdmt_channel_update_watch (ct);
+      return TRUE;
+  }
+  while (!g_queue_is_empty (q))
+      gdmt_send_exit_action (ct, GPOINTER_TO_INT (g_queue_pop_head (q))); 
+  g_queue_free (q);
+  gdmt_write_enable(ct);
+  gdmt_read_enable(ct);
+
+  return TRUE;
+}
+
+
+void
+gdmt_send_exit_action (GDMTContext *ct, GDMTGdmCommand cmd)
+{
+  const gchar *t;
+  g_assert (ct != NULL);
+  GDMTChannelState prereq, current;
+
+  current = ct->channel_state;
+  if (cmd < 0  ||  cmd >= GDMT_GDMCMD_N  ||  current & GDMT_CHANNL_SHUTDOWN)
+      return;
+  prereq = gdmt_gdmcmd_prerequisite[cmd];
+
+  /* open connection to GDM on demand */
+  if (prereq & GDMT_CHANNL_OPEN  &&  !(current & GDMT_CHANNL_OPEN))
+      gdmt_socket_open (ct, GDM_SOCKETNAME);
+      
+  /* authenticate with GDM on demand */
+  if (prereq & GDMT_CHANNL_AUTH7ED  &&  !(current & GDMT_CHANNL_AUTH7ED))
+      gdmt_send_exit_action (ct, GDMT_GDMCMD_AUTH);
+
+  t = ct->send_strings[cmd];
+  if (t == NULL)
+      return;
+
+  if (cmd == GDMT_GDMCMD_AUTH)
+      ct->channel_state |= GDMT_CHANNL_AUTH7ED;
+  gdmt_push_cmd (ct, cmd);
+  gdmt_queue_line (ct, t);
+  gdmt_channel_update_watch (ct);
+}
+
+
+
+
+/*** Chapter 2: Low level Read/Write Management
+ *.............................................................
+ */
+
+
+gboolean
+gdmt_channel_update_watch (GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+  if (gdmt_is (ct, GDMT_CHANNL_SHUTDOWN))
+      ct->cond_next = 0;
+  if (ct->cond_next == ct->cond_current)
+      return TRUE;
+
+  gdmt_ref (ct); /* For g_io_add_watch_full(). Keep before g_source_remove(). */
+  if (!gdmt_is (ct, GDMT_CHANNL_IN_IO)  &&  ct->cond_current != 0)
+      g_source_remove (ct->source_id);
+
+  ct->cond_current = ct->cond_next;
+  if (ct->cond_next == 0) {
+	 gdmt_unref (ct); /* undo gdmt_ref() from above */
+      return FALSE;
+  }
+
+  /* gdmt_ref() moved in front of g_source_remove() to not let ref_count
+     drop to 0 temporarily (every watch holds one reference to ct) */
+  ct->source_id = g_io_add_watch_full (ct->channel, G_PRIORITY_DEFAULT,
+		       ct->cond_next, (GIOFunc)&gdmt_io_handler, ct,
+		       (GDestroyNotify)&gdmt_unref);
+  
+  return FALSE;
+}
+
+static void
+gdmt_write_enable(GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+
+  if (ct->channel_state & GDMT_CHANNL_WRITEABLE)
+      ct->cond_next |= G_IO_OUT;
+}
+
+
+static void
+gdmt_read_enable(GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+      
+  if (ct->channel_state & GDMT_CHANNL_READABLE)
+      ct->cond_next |= G_IO_IN;
+}
+
+
+static void
+gdmt_write_disable(GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+      
+  ct->cond_next &= ~G_IO_OUT;
+}
+
+
+static void
+gdmt_read_disable(GDMTContext *ct)
+{
+  g_assert (ct != NULL);
+      
+  ct->cond_next &= ~G_IO_IN;
+}
+
+
+static gboolean
+gdmt_io_handler (GIOChannel *source, GIOCondition condition,
+		gpointer data)
+{
+  GDMTContext *ct = data;
+  gboolean retval;
+
+  g_assert (ct != NULL);
+  ct->channel_state |= GDMT_CHANNL_IN_IO;
+  
+
+  if (gdmt_is (ct, GDMT_CHANNL_SHUTDOWN))
+      goto spaghetti_shutdown;
+
+  if (condition & G_IO_HUP) {
+      ct->channel_state &= ~GDMT_CHANNL_WRITEABLE;
+      ct->cond_next &= ~(G_IO_HUP | G_IO_OUT);
+  }
+
+  if (condition & G_IO_OUT) {
+      if (!fbu_drain (&ct->write_buffer) || gdmt_is(ct, GDMT_CHANNL_SHUTDOWN))
+	       goto spaghetti_shutdown;
+      if (!fbu_refill (&ct->write_buffer)|| gdmt_is(ct, GDMT_CHANNL_SHUTDOWN))
+	       goto spaghetti_shutdown;
+  }
+
+  if (condition & G_IO_IN) { /* reverse sequence of drain and refill */
+      if (!fbu_refill (&ct->read_buffer) || gdmt_is(ct, GDMT_CHANNL_SHUTDOWN))
+	       goto spaghetti_shutdown;
+      if (!fbu_drain (&ct->read_buffer)  || gdmt_is(ct, GDMT_CHANNL_SHUTDOWN))
+	       goto spaghetti_shutdown;
+  }
+
+  if (condition & (G_IO_PRI | G_IO_ERR | G_IO_NVAL))
+      goto spaghetti_shutdown;
+
+  if (!(ct->channel_state & GDMT_CHANNL_READABLE))
+      goto spaghetti_shutdown;
+
+  retval =  gdmt_channel_update_watch (ct);
+  ct->channel_state &= ~GDMT_CHANNL_IN_IO;
+  return retval;
+
+spaghetti_shutdown:
+  gdmt_shutdown (ct);
+  ct->channel_state &= ~GDMT_CHANNL_IN_IO;
+  return FALSE;
+}
+
+
+/* Drain Write Buffer: write data from write buffer to file.
+   This function is called on a chunk (e.g. a line), not neccessarily the
+   entire buffer  */
+static gboolean
+gdmt_fbu_write_drain (gchar *line, gsize *len, FlowBuffer *fb)
+{
+  GDMTContext *ct;
+  GIOStatus status;
+  GError *error = NULL;
+
+  g_assert (line!=NULL  &&  len!=NULL  &&  fb!=NULL  &&  fb->user_data!=NULL);
+
+  ct = fb->user_data;
+  status = g_io_channel_write_chars (ct->channel, line, *len, len, &error);
+
+  if (status != G_IO_STATUS_ERROR  &&  error == NULL)
+      return TRUE;
+
+  if (error != NULL  &&  fb->error_logger != NULL)
+      (*fb->error_logger) ("gdmt_fb_write_drain, %s: %s\n",
+			    fb->name, error->message);
+  return FALSE;
+}
+
+
+/* Refill Write Buffer: do nothing, only remove file descriptor from set
+   of poll()'ed writeable file descriptors if write buffer is empty anyway */
+static gboolean
+gdmt_fbu_write_refill (FlowBuffer *eb)
+{
+  g_assert (eb != NULL  &&  eb->buff != NULL);
+
+  if (eb->buff->len == 0)
+      gdmt_write_disable(eb->user_data);
+
+  return TRUE;
+}
+
+
+/* Refill Read Buffer: read data from file into read buffer, growing buffer
+   beforehand if necessary */
+static gboolean
+gdmt_fbu_read_refill (FlowBuffer *fb)
+{
+  GString *s;
+  gsize n;
+  gchar *p;
+  GError *error = NULL;
+  GIOStatus status;
+  GDMTContext *ct;
+
+  g_assert (fb != NULL  &&  fb->user_data != NULL);
+
+  /* Glossary: headroom = (buffer->allocated_len - 1) - buffer->len */
+  fbu_grow_buffer (fb, 63); /* grows only if headroom < 63 */
+  s = fb->buff;
+
+  /* Append new data to stuff already in buffer */
+  p = s->str + s->len; 		       /* start of free space */
+  n = s->allocated_len - 1 - s->len;  /* headroom */
+  g_assert (n >= 0);
+  if (n == 0)
+      return FALSE;
+
+  ct = fb->user_data;
+  g_assert (ct->channel != NULL);
+  status = g_io_channel_read_chars (ct->channel, p, n, &n, &error);
+  if ((status==G_IO_STATUS_NORMAL || status==G_IO_STATUS_AGAIN) 
+	       &&  error == NULL) {
+      g_assert (n >= 0);
+      s->len += n;
+      s->str[s->len] = '\0';
+      return TRUE;
+  }
+  if (status==G_IO_STATUS_EOF) {
+      /* connection closed for writing by GDM */
+      ct->channel_state &= ~GDMT_CHANNL_READABLE;
+      ct->cond_next &= ~G_IO_IN;
+      return TRUE;
+  }
+
+  if (error != NULL  &&  fb->error_logger != NULL)
+      (*fb->error_logger) (fb->error_logger_data,
+			    fb->name, error->message);
+  return FALSE;
+}
+
+
+/* Drain Read Buffer: transfer data from read buffer to parser of GDM traffic.
+   This function is called on a chunk (e.g. a line), not neccessarily the
+   entire buffer  */
+static gboolean
+gdmt_fbu_read_drain (gchar *line, gsize *len, FlowBuffer *fb)
+{
+  GDMTContext *ct;
+
+  g_assert (line!=NULL  &&  len!=NULL  &&  fb!=NULL  &&  fb->user_data!=NULL);
+
+  ct = fb->user_data;
+  return gdmt_gdm_response_handler (line, *len, ct);
+}
+
+
+
+
+/*** Chapter 3: High level Read/Write data processing
+ *.............................................................
+ */
+
+
+static gboolean
+gdmt_gdm_response_handler (gchar *line, gsize length, GDMTContext *ct)
+{
+  gint			error_code = 0;
+  gchar			*error = NULL;
+  GDMTGdmCommand	old_cmd;
+
+  g_assert (ct != NULL);
+
+  /* don't remove command from queue yet, it might have to be re-issued */
+  old_cmd = gdmt_peek_cmd (ct);
+
+
+  error_code = gdmt_parse_gdm_response1(&line);
+  if (error_code) {
+	error = g_strdup_printf ("Error sending command '%s' to GDM\n",
+				 gdmt_gdmcmd_to_debug[old_cmd]);
+	goto spaghetti_error;
+  }
+
+  if (old_cmd == GDMT_GDMCMD_QUERY) {
+  	struct cmp_list_t {
+		const gchar		*token;
+		const guint		length;
+		const GDMTActions	action;
+	};
+  	static struct cmp_list_t args[] = {
+	   { GDM_SUP_LOGOUT_ACTION_HALT,
+	   	sizeof(GDM_SUP_LOGOUT_ACTION_HALT) - 1,
+		GDMT_ACTION_HALT
+	   },
+	   { GDM_SUP_LOGOUT_ACTION_REBOOT,
+		sizeof(GDM_SUP_LOGOUT_ACTION_REBOOT) - 1,
+		GDMT_ACTION_REBOOT
+	   },
+	   { GDM_SUP_LOGOUT_ACTION_SUSPEND,
+		sizeof(GDM_SUP_LOGOUT_ACTION_SUSPEND) - 1,
+		GDMT_ACTION_SUSPEND
+	   },
+	   { NULL,
+	   	0,
+		0
+	   }
+	};
+	struct cmp_list_t *arg = &args[0];
+	gdmt_available_actions = GDMT_ACTION_NONE;
+	while (*line != '\0') {
+		guint n = (sizeof(args) / sizeof(args[0])) - 1;
+		do {
+			guint len = arg->length;
+			if (0 == g_ascii_strncasecmp(line, arg->token, len)) {
+				guint tmp_action = gdmt_default_action;
+				line += len;
+				if (*line == '!') {
+					tmp_action = arg->action;
+					line++;
+				}
+				if (*line == ';'  ||  *line == '\0') {
+					gdmt_available_actions |= arg->action;
+					gdmt_default_action = tmp_action;
+				} else {
+					/* abort */
+					*line = 0;
+					error_code = -1;
+					break;
+				}
+				if (*line == ';')
+					line++;
+				/* last array iteration (must reload n) */
+				n = 1;
+			}
+			if ((++arg)->token == NULL)
+				arg = &args[0];
+		} while G_UNLIKELY (--n);
+	}
+  }
+
+  /* remove command from queue now, it has been successful */
+  gdmt_pop_cmd (ct);
+
+  if (ct->got_response != NULL) {
+	(*ct->got_response) (ct, old_cmd, line, ct->got_response_user_data);
+	if (gdmt_is (ct, GDMT_CHANNL_IDLE)  &&  ct->got_response != NULL)
+		(*ct->got_response) (ct, GDMT_GDMCMD_IDLE, 0,
+				     ct->got_response_user_data);
+  }
+  return TRUE;
+
+spaghetti_error:
+  /* check for GDM's error code for too-many-commands-per-connection */
+  if (error_code == 200) {
+	/* yo, re-open the connection */
+	gdmt_socket_open (ct, GDM_SOCKETNAME);
+
+	if (!gdmt_is (ct, GDMT_CHANNL_SHUTDOWN)) {
+		g_free (error);
+		return TRUE;  /* try again */
+	}
+  }
+
+  if (error_code == -1  &&  ct->error_logger != NULL) {
+	(*ct->error_logger) ("Could not parse GDM's response to "
+				"'%s'\n", gdmt_gdmcmd_to_debug[old_cmd]);
+	goto spaghetti_error;
+  }
+
+  /* print error message */
+  if (error != NULL  &&  ct->error_logger != NULL)
+      (*ct->error_logger) ("%s (%s)\n", error, line);
+
+  g_free (error);
+  gdmt_shutdown (ct);
+  return FALSE;
+}
+
+
+/* check for first token ("OK" or "ERROR")
+ * return  0 in case of no error,
+ *        -1 in case of parsing error
+ *        or error_code from GDM
+ */
+static gint
+gdmt_parse_gdm_response1(gchar **buffer)
+{
+  gchar *token, *nextc;
+  gint error_code;
+
+  token = ascii_strsep (buffer, " ");
+  if (token == NULL)
+	return -1;
+  if (g_ascii_strcasecmp (token, "OK") == 0)
+	return 0;
+  if (g_ascii_strcasecmp (token, "ERROR") != 0)
+  	return -1;
+
+  /* "ERROR" is followed by <error code> <error text> */
+  token = ascii_strsep (buffer, " ");
+  error_code = g_ascii_strtoull (token, &nextc, 10);
+  if (nextc <= token)
+	return -1;
+
+  return error_code;
+}
+
+
+static void
+gdmt_queue_line (GDMTContext *context, const char* line)
+{
+  FlowBuffer *fb;
+  g_assert (context != NULL  &&  line != NULL);
+  if (!(context->channel_state & GDMT_CHANNL_WRITEABLE))
+      return;
+
+  fb = &context->write_buffer;
+  g_assert (fb != NULL);
+
+  fbu_grow_buffer (fb, 0); /* allocate new buffer if not done yet */
+
+  g_string_append_printf (fb->buff, "%s\n", line);
+
+  gdmt_write_enable(context);
+  gdmt_read_enable(context);
+}
+
+
+
+
+/*** Chapter 4: Command Queue
+ *.............................................................
+ */
+
+
+static void
+gdmt_push_cmd (GDMTContext *ct, GDMTGdmCommand cmd)
+{
+  g_assert (ct != NULL);
+  if (ct->channel_state & GDMT_CHANNL_SHUTDOWN)
+      return;
+  g_queue_push_tail (ct->command_queue, GINT_TO_POINTER(cmd));
+}
+
+
+static void
+gdmt_flush_cmd (GDMTContext *ct)
+{
+  g_assert (ct != NULL  &&  ct->command_queue != NULL);
+
+  while (!g_queue_is_empty (ct->command_queue))
+      g_queue_pop_head (ct->command_queue);
+}
+
+
+static GDMTGdmCommand
+gdmt_pop_cmd (GDMTContext *ct)
+{
+  g_assert (ct != NULL  &&  ct->command_queue != NULL);
+
+  if (ct->channel_state & GDMT_CHANNL_SHUTDOWN)
+      return GDMT_GDMCMD_CLOSE;
+  if (g_queue_is_empty (ct->command_queue))
+      return GDMT_CHANNL_IDLE;
+  return GPOINTER_TO_INT (g_queue_pop_head (ct->command_queue));
+}
+
+
+static GDMTGdmCommand
+gdmt_peek_cmd (GDMTContext *ct)
+{
+  g_assert (ct != NULL  &&  ct->command_queue != NULL);
+
+  if (ct->channel_state & GDMT_CHANNL_SHUTDOWN)
+      return GDMT_GDMCMD_CLOSE;
+  if (g_queue_is_empty (ct->command_queue))
+      return GDMT_CHANNL_IDLE;
+  return GPOINTER_TO_INT (g_queue_peek_head (ct->command_queue));
+}
+
+
+gboolean
+gdmt_is (GDMTContext *ct, GDMTChannelState flags)
+{
+  GDMTChannelState current;
+  g_assert (ct != NULL);
+  
+  current = ct->channel_state  &  ~GDMT_CHANNL_IDLE;
+  if (flags & GDMT_CHANNL_IDLE) {
+      g_assert (ct->command_queue != NULL);
+      if (g_queue_is_empty (ct->command_queue))
+	       current |= GDMT_CHANNL_IDLE;
+  }
+
+  return (current & flags)  ==  flags;
+}
+
+
+/*** Chapter 5: Authentication
+ *.............................................................
+ */
+
+
+#define MAX_HOSTNAME_BUFFER   1024
+
+#define MIT_COOKIE_NAME	       "MIT-MAGIC-COOKIE-1"
+#define MIT_COOKIE_NAME_LENGTH        18
+
+#define xau_types_length (sizeof(xau_types)/sizeof(const char*))
+
+static const char* const xau_types[]	     = { MIT_COOKIE_NAME };
+static const int	  xau_type_lengths[] = { MIT_COOKIE_NAME_LENGTH };
+
+
+static char *
+get_mit_cookie(gchar *buffer, gint buffer_length)
+{
+  Xauth *auth;
+  const gchar *displ_No;      /* display number string and...*/
+  gsize displ_len;	       /* ...it's length */
+  char hostname[MAX_HOSTNAME_BUFFER];
+
+  const gchar *displayname, *p;
+  GdkDisplay *display;
+
+  g_assert (buffer_length >= 2*MIT_COOKIE_BYTE_LENGTH);
+
+  display     = gdk_display_get_default ();
+  displayname = gdk_display_get_name (display);
+
+  p = displayname;
+  if (strncmp (p, "unix", 4) == 0)
+      p += 4;
+  if (p++[0] != ':')
+      return g_strconcat ("Display ", displayname, " is not 100% "
+			   "local or not valid.", NULL);
+      
+  errno = 0;
+  hostname[MAX_HOSTNAME_BUFFER - 1] = '\0';
+  if (gethostname (hostname, MAX_HOSTNAME_BUFFER - 1) != 0)
+      return g_strconcat ("Could not read hostname: ", g_strerror (errno),
+			   NULL);
+
+  displ_No = p;
+  for (displ_len = 0; g_ascii_isdigit(*p)  &&  displ_len < 20; displ_len++)
+      p++;
+
+  auth = XauGetBestAuthByAddr (FamilyLocal, strlen(hostname), hostname,
+			      displ_len, displ_No, xau_types_length,
+			      (char**)xau_types, xau_type_lengths);
+
+  if (auth == NULL)
+      return g_strdup_printf ("No local MIT-MAGIC-COOKIE found on %s:%s.",
+			       hostname, displ_No);
+
+  if (auth->family != FamilyLocal					 ||
+      auth->address_length != strlen(hostname) 				 ||
+      strncmp (auth->address, hostname, auth->address_length) != 0	 ||
+      auth->name_length != MIT_COOKIE_NAME_LENGTH			 ||
+      strncmp (auth->name, MIT_COOKIE_NAME, MIT_COOKIE_NAME_LENGTH) != 0  ||
+      auth->data_length != MIT_COOKIE_BYTE_LENGTH)
+      return g_strdup_printf ("No matching local MIT-MAGIC-COOKIE found on "
+			       "%s:%s.", hostname, displ_No);
+
+  bin2hex(MIT_COOKIE_BYTE_LENGTH, auth->data, buffer);
+      
+  XauDisposeAuth (auth);
+  return NULL;
+}
+
+
+
+
+/*** Chapter 6: Logging functions (fallback)
+ *.............................................................
+ */
+
+
+static void
+gdmt_message_critical (const gchar *format, ...)
+{
+  va_list va;
+  va_start (va, format);
+  g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, va);
+  va_end (va);
+}
+
+
+
+
+/*** Appendix A: String utility functions
+ *.............................................................
+ */
+
+/* must match reverse table in GDM */
+static const char* const bin2hex_nibbles = "0123456789abcdef";
+
+static void
+bin2hex(unsigned int binlen, const char *bindata, char *hexdata)
+{
+      for (; binlen > 0; binlen--) {
+	       const unsigned int s = *bindata++;
+	       *hexdata++ = bin2hex_nibbles[(s>>4) & 0xf];
+	       *hexdata++ = bin2hex_nibbles[s & 0xf];
+      }
+}
+
+
+static gchar*
+ascii_strsep (gchar **string_ptr, const gchar *delimiters)
+{
+  gchar *token;
+  g_assert (string_ptr != NULL  &&  delimiters != NULL);
+  token = *string_ptr;
+  if (token == NULL)
+	return token;
+
+  /* strpbrk must return NULL if delimiters is a zero length string */
+  *string_ptr = strpbrk (token, delimiters);
+  if (*string_ptr != NULL) {
+	**string_ptr = '\0';
+	++*string_ptr;
+  }
+
+  return token;
+}
+
+
+
+
+/*** Appendix B: Flow Buffer functions
+ *.............................................................
+ */
+
+
+static gboolean
+fbu_grow_buffer (FlowBuffer *fb, gsize min_headroom)
+{
+  GString *s;
+  gsize old_len, new_len;
+  gboolean ok = TRUE;
+  
+  g_assert (fb != NULL);
+      
+  s = fb->buff;
+  if (s == NULL) {
+      if (min_headroom == 0)
+	       min_headroom = 63;
+      if (min_headroom > fb->max_size) {
+	       min_headroom = fb->max_size;
+	       ok =  FALSE;
+      }
+      fb->buff = g_string_sized_new (min_headroom);
+      return ok;
+  }
+ 
+  new_len = s->len + min_headroom;
+  if (new_len > fb->max_size) {
+      new_len = fb->max_size;
+      ok = FALSE;
+  }
+
+  if (new_len >= s->allocated_len) {
+      old_len = s->len;
+      g_string_set_size (s, new_len);
+      s->str[s->len] = '\0';
+      s->len = old_len;
+  }
+
+  return ok;
+}
+
+
+static gboolean
+fbu_drain (FlowBuffer *fb)
+{
+  gchar *t, *u;
+  gsize n;
+  GString *s;
+  gboolean ok = FALSE;
+
+  g_assert (fb != NULL);
+
+  fbu_grow_buffer (fb, 0); /* allocate new buffer if not done yet */
+  s = fb->buff;
+  if (s->len == 0)
+      ok = 1; /* don't die if fbu_refill() didn't refill empty buffer */
+
+  t = s->str;
+  /* call drain callback function for each line (text + line terminator) */
+  while (t < (u = s->str + s->len)) {
+
+      /* go to end of line instead of end of string ? */
+      if (fb->drain_delimiter != NULL) {
+	       u = g_strstr_len (t, u-t, fb->drain_delimiter);
+	       if (u == NULL)
+		       break;
+      }
+
+      /* Yay, there is a complete line, reaching from t-u. */
+      *u = 0;
+      g_assert (fb->drain != NULL);
+      n = u - t;
+	/* normalize t with respect to s->str */
+      t = GINT_TO_POINTER (t - s->str);
+      ok = (*fb->drain) (s->str + GPOINTER_TO_INT (t), &n, fb);
+	/* denormalize t with respect to (the new) s->str */
+      t = s->str + GPOINTER_TO_INT (t);
+      if (!ok)
+	       break;
+      t += n;
+      if (fb->drain_delimiter != NULL)
+	       t += strlen (fb->drain_delimiter);
+  } /* u must be set even if while body not executed at least once */
+
+  /* remove old data from buffer (move pending data to beginning) */
+  g_string_erase (s, 0, t - s->str);
+
+
+  if (!ok  ||  s->len < s->allocated_len - 1)
+      return ok;
+
+  return (s->len < s->allocated_len - 1)  ||  (u != NULL);
+  /* return FALSE if 'buffer overflow':
+     buffer>=max_size but no chance to drain because no line terminator found */
+}
+
+
+static gboolean
+fbu_refill (FlowBuffer *fb)
+{
+  g_assert (fb != NULL  &&  fb->refill != NULL);
+
+  fbu_grow_buffer (fb, 63); /* grows only if headroom < 63 */
+  return (*fb->refill) (fb);
+}
+
+
+
+
+/* eof */
diff -urNad gnome-session-2.6.2.halfpatched/gnome-session/gdm-talk.h gnome-session-2.6.2/gnome-session/gdm-talk.h
--- gnome-session-2.6.2.halfpatched/gnome-session/gdm-talk.h	1970-01-01 01:00:00.000000000 +0100
+++ gnome-session-2.6.2/gnome-session/gdm-talk.h	2004-07-25 17:04:48.000000000 +0200
@@ -0,0 +1,93 @@
+/* gdm-talk.h - Talk to GDM about session followup actions.
+
+   Written by Jarno Gassenbauer <jarno@gmx.net>
+   Copyright (C) 2003 Jarno Gassenbauer
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#ifndef GDM_TALK_H
+#define GDM_TALK_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _GDMTContext GDMTContext;
+typedef struct _FlowBuffer FlowBuffer;
+
+/* list of commands to be sent to GDM */
+/* table 'gdmt_gdmcmd_to_string'     depends on this */
+/* table 'gdmt_gdmcmd_prerequisite'  depends on this */
+/* table 'gdmt_gdmcmd_to_debug'      depends on this */
+typedef enum {
+  GDMT_GDMCMD_VERSION = 0,  /* query version string */
+  GDMT_GDMCMD_AUTH,	    /* authenticate with GDM */
+  GDMT_GDMCMD_QUERY,	    /* Query GDM for valid and default logout actions */
+  GDMT_GDMCMD_NONE,	    /* set logout action to 'none' */
+  GDMT_GDMCMD_HALT,	    /* set logout action to 'halt' */
+  GDMT_GDMCMD_REBOOT,	    /* set logout action to 'reboot' */
+  GDMT_GDMCMD_SUSPEND,	    /* set logout action to 'suspend' */
+  GDMT_GDMCMD_CLOSE,	    /* close connection */
+  GDMT_GDMCMD_IDLE,	    /* all commands acknowledged */
+  GDMT_GDMCMD_N		    /* internally used (count of GDM commands) */
+}  GDMTGdmCommand;
+
+typedef enum {
+  GDMT_CHANNL_OPEN	= 1<<0,	/* connection to GDM established */
+  GDMT_CHANNL_AUTH7ED	= 1<<1,	/* authentication successful */
+  GDMT_CHANNL_WRITEABLE	= 1<<2,	/* not hung up for write */
+  GDMT_CHANNL_READABLE	= 1<<3,	/* not hung up for read */
+  GDMT_CHANNL_SHUTDOWN	= 1<<4,	/* closing connection, unrecoverable */
+  GDMT_CHANNL_IDLE	= 1<<5,	/* pseudo flag, true if command_queue empty */
+  GDMT_CHANNL_IN_IO	= 1<<6	/* in channel IO handler */
+}  GDMTChannelState;
+
+typedef enum {
+  GDMT_ACTION_NONE	= 1<<0,
+  GDMT_ACTION_HALT	= 1<<1,
+  GDMT_ACTION_REBOOT	= 1<<2,
+  GDMT_ACTION_SUSPEND	= 1<<3,
+} GDMTActions;
+
+extern GDMTActions gdmt_default_action;
+
+extern const gchar* const gdmt_gdmcmd_to_debug[];
+
+extern unsigned int gdmt_available_actions;
+
+typedef void	 (*GDMTErrorLoggerFunc) (gpointer	logger_data, ...);
+typedef void	 (*GDMTGotResponseFunc) (GDMTContext	*context,
+					 GDMTGdmCommand	cmd,
+					 gchar		*line,
+					 gpointer	user_data);
+
+GDMTContext*  gdmt_startup  (GDMTErrorLoggerFunc	err_func,
+			     GDMTGotResponseFunc	handler,
+			     gpointer			handler_data);
+void	      gdmt_shutdown (GDMTContext *ct);
+void	      gdmt_ref      (GDMTContext *ct);
+void	      gdmt_unref    (GDMTContext *ct);
+void	      gdmt_send_exit_action (GDMTContext *ct, GDMTGdmCommand cmd);
+gboolean      gdmt_running  (GDMTContext *ct);
+
+
+G_END_DECLS
+
+
+#endif /* GDM_TALK_H */
+
+/* eof */
diff -urNad gnome-session-2.6.2.halfpatched/gnome-session/logout.c gnome-session-2.6.2/gnome-session/logout.c
--- gnome-session-2.6.2.halfpatched/gnome-session/logout.c	2004-04-19 14:48:11.000000000 +0200
+++ gnome-session-2.6.2/gnome-session/logout.c	2004-07-25 17:06:07.000000000 +0200
@@ -34,6 +34,7 @@
 #include "util.h"
 #include "gsm-multiscreen.h"
 #include "egg-screen-help.h"
+#include "gdm-talk.h"
 
 static gchar *halt_command[] =
 {
@@ -45,15 +46,42 @@
   REBOOT_COMMAND, NULL
 };
 
+#ifdef NO_GDMTALK_SHUTDOWN
+#define GDMTALK_SHUTDOWN      FALSE
+#else
+#define GDMTALK_SHUTDOWN      TRUE
+#endif
+
+#ifdef NO_REDHAT_SHUTDOWN
+#define REDHAT_SHUTDOWN	       FALSE
+#else
+#define REDHAT_SHUTDOWN	       TRUE
+#endif
+
+      /* shutdown through talking to gdm */
+static gboolean gdmtalk_shutdown = GDMTALK_SHUTDOWN;
+      /* red hat style shutdown functionality */
+static gboolean redhat_shutdown  = REDHAT_SHUTDOWN;
+static GDMTContext  *gdmt_ct = NULL;
+      /* radio check button inside 'action' frame */
+static GtkWidget *logout = NULL;
+static GtkWidget *halt = NULL;
+static GtkWidget *reboot = NULL;
+static GtkWidget *suspend = NULL;
+static GtkWidget *ok_button = NULL;
 /* What action to take upon shutdown */
 static enum
 {
   LOGOUT,
   HALT,
-  REBOOT
+  REBOOT,
+  SUSPEND
 }
 action = LOGOUT;
 
+static void set_exit_action (GtkToggleButton *button, gpointer user_data);
+
+
 typedef struct {
   GdkScreen    *screen;
   int           monitor;
@@ -286,6 +314,129 @@
   return label;
 }
 
+
+static void
+gdmt_handler (GDMTContext  *context, GDMTGdmCommand cmd,
+	     gchar *line, gpointer user_data)
+{
+  if (context == NULL  ||  gdmt_ct == NULL)
+    {
+      if (logout != NULL)
+	 g_object_set (G_OBJECT (logout), "sensitive", FALSE, NULL);
+      if (halt != NULL)
+	 g_object_set (G_OBJECT (halt), "sensitive", FALSE, NULL);
+      if (reboot != NULL)
+	 g_object_set (G_OBJECT (reboot), "sensitive", FALSE, NULL);
+      if (suspend != NULL)
+	 g_object_set (G_OBJECT (suspend), "sensitive", FALSE, NULL);
+      if (gdmt_ct != NULL)
+	 gdmt_shutdown (gdmt_ct);
+      return;
+    }
+  
+  switch (cmd) {
+    case GDMT_GDMCMD_AUTH:
+      gsm_verbose ("Authentication with GDM successful.\n");
+      break;
+    case GDMT_GDMCMD_QUERY:
+      if (logout != NULL)
+	g_object_set (G_OBJECT (logout), "sensitive",
+		      (gdmt_available_actions & GDMT_ACTION_NONE) ? TRUE:FALSE, NULL);
+      if (halt != NULL)
+	g_object_set (G_OBJECT (halt), "sensitive",
+		      (gdmt_available_actions & GDMT_ACTION_HALT) ? TRUE:FALSE, NULL);
+      if (reboot != NULL)
+	g_object_set (G_OBJECT (reboot), "sensitive",
+		      (gdmt_available_actions & GDMT_ACTION_REBOOT) ? TRUE:FALSE, NULL);
+      if (suspend != NULL)
+	g_object_set (G_OBJECT (suspend), "sensitive",
+		      (gdmt_available_actions & GDMT_ACTION_SUSPEND) ? TRUE:FALSE, NULL);
+
+      if (gdmt_default_action == GDMT_ACTION_NONE  &&  logout != NULL)
+	g_object_set (G_OBJECT (logout), "active", TRUE, NULL);
+      else if (gdmt_default_action == GDMT_ACTION_HALT  &&  halt != NULL)
+	g_object_set (G_OBJECT (halt), "active", TRUE, NULL);
+      else if (gdmt_default_action == GDMT_ACTION_REBOOT  &&  reboot != NULL)
+	g_object_set (G_OBJECT (reboot), "active", TRUE, NULL);
+      else if (gdmt_default_action == GDMT_ACTION_SUSPEND  &&  suspend != NULL)
+	g_object_set (G_OBJECT (suspend), "active", TRUE, NULL);
+
+      break;
+    case GDMT_GDMCMD_NONE:
+      gdmt_default_action = GDMT_ACTION_NONE;
+      if (logout != NULL)
+	 {
+	   g_object_set (G_OBJECT (logout), "sensitive", TRUE, NULL);
+	   g_object_set (G_OBJECT (logout), "active", TRUE, NULL);
+	 }
+      if (ok_button != NULL)
+        g_object_set (G_OBJECT (ok_button), "sensitive", TRUE, NULL);
+      break;
+    case GDMT_GDMCMD_HALT:
+      gdmt_default_action = GDMT_ACTION_HALT;
+      if (halt != NULL)
+	 {
+	   g_object_set (G_OBJECT (halt), "sensitive", TRUE, NULL);
+	   g_object_set (G_OBJECT (halt), "active", TRUE, NULL);
+	 }
+      if (ok_button != NULL)
+        g_object_set (G_OBJECT (ok_button), "sensitive", TRUE, NULL);
+      break;
+    case GDMT_GDMCMD_REBOOT:
+      gdmt_default_action = GDMT_ACTION_REBOOT;
+      if (reboot != NULL)
+	 {
+	   g_object_set (G_OBJECT (reboot), "sensitive", TRUE, NULL);
+	   g_object_set (G_OBJECT (reboot), "active", TRUE, NULL);
+	 }
+      if (ok_button != NULL)
+        g_object_set (G_OBJECT (ok_button), "sensitive", TRUE, NULL);
+      break;
+    case GDMT_GDMCMD_SUSPEND:
+      gdmt_default_action = GDMT_ACTION_SUSPEND;
+      if (suspend != NULL)
+	 {
+	   g_object_set (G_OBJECT (suspend), "sensitive", TRUE, NULL);
+	   g_object_set (G_OBJECT (suspend), "active", TRUE, NULL);
+	 }
+      if (ok_button != NULL)
+        g_object_set (G_OBJECT (ok_button), "sensitive", TRUE, NULL);
+      break;
+  case GDMT_GDMCMD_CLOSE:
+      gsm_verbose ("Connection to GDM died or been closed.\n");
+      break;
+  case GDMT_GDMCMD_IDLE:
+      gsm_verbose ("Connection to GDM idle.\n");
+      break;
+  default:
+      if (gdmt_ct != NULL)
+	 gdmt_shutdown (gdmt_ct);
+  }
+}
+
+
+static void
+set_exit_action (GtkToggleButton *button, gpointer user_data)
+{
+  GDMTGdmCommand action = GPOINTER_TO_INT (user_data);
+  const gchar *t;
+
+  if (!GTK_TOGGLE_BUTTON (button)->active)
+    {
+      g_object_set (G_OBJECT (button), "active", FALSE, NULL);
+      return;
+    }
+  t = gdmt_gdmcmd_to_debug[action];
+  if (t == NULL)
+    t = "<unknown>";
+  gsm_verbose ("set_exit_action %s\n", t);
+
+  if (ok_button != NULL)
+    g_object_set (G_OBJECT (ok_button), "sensitive", FALSE, NULL);
+  gdmt_send_exit_action (gdmt_ct, action);
+}
+
+
 static gboolean
 display_gui (void)
 {
@@ -297,8 +448,6 @@
   GtkWidget *toggle_button = NULL;
   gint response;
   gchar *s, *t;
-  GtkWidget *halt = NULL;
-  GtkWidget *reboot = NULL;
   GtkWidget *invisible;
   gboolean   retval = FALSE;
   gboolean save_active = FALSE;
@@ -311,6 +460,14 @@
 
   gsm_verbose ("display_gui: showing logout dialog\n");
 
+  gdmt_ct = NULL;
+  logout = NULL;
+  halt = NULL;
+  reboot = NULL;
+  suspend = NULL;
+  gdmtalk_shutdown = GDMTALK_SHUTDOWN;
+  redhat_shutdown  = REDHAT_SHUTDOWN;
+
   /* It's really bad here if someone else has the pointer
    * grabbed, so we first grab the pointer and keyboard
    * to an offscreen window, and then once we have the
@@ -378,7 +535,7 @@
   
   gtk_dialog_add_button (GTK_DIALOG (box), GTK_STOCK_HELP, GTK_RESPONSE_HELP);
   gtk_dialog_add_button (GTK_DIALOG (box), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
-  gtk_dialog_add_button (GTK_DIALOG (box), GTK_STOCK_OK, GTK_RESPONSE_OK);
+  ok_button = gtk_dialog_add_button (GTK_DIALOG (box), GTK_STOCK_OK, GTK_RESPONSE_OK);
 
   gtk_dialog_set_default_response (GTK_DIALOG (box), GTK_RESPONSE_OK);
   gtk_window_set_screen (GTK_WINDOW (box), screen);
@@ -395,14 +552,36 @@
 			  FALSE, TRUE, 0);
     }
 
-  /* Red Hat specific code to check if the user has a
-   * good chance of being able to shutdown the system,
-   * and if so, give them that option
-   */
-  s = g_strconcat ("/var/lock/console/", g_get_user_name (), NULL);
-  t = g_strconcat ("/var/run/console/", g_get_user_name (), NULL);
-  if (((geteuid () == 0) || g_file_exists (t) || g_file_exists(s)) &&
-      access (halt_command[0], X_OK) == 0)
+  if (gdmtalk_shutdown)
+    {
+      gdmt_ct = gdmt_startup (NULL, &gdmt_handler, NULL);
+      if (gdmt_ct == NULL)
+	 gdmtalk_shutdown = FALSE;
+      else {
+	 /* this command will be queued, it does not block the mainloop */
+         /* if connection dies during transmission, all actions that have
+	    not been acknowledged yet will be sent again on a new connection */
+	 gdmt_send_exit_action (gdmt_ct, GDMT_GDMCMD_QUERY);
+      }
+    }
+
+  if (redhat_shutdown)
+    {
+      /* Red Hat specific code to check if the user has a
+	* good chance of being able to shutdown the system,
+	* and if so, give them that option
+	*/
+      s = g_strconcat ("/var/lock/console/", g_get_user_name (), NULL);
+      t = g_strconcat ("/var/run/console/", g_get_user_name (), NULL);
+      if (((geteuid () != 0) && !g_file_exists (t) && !g_file_exists(s)) ||
+	   access (halt_command[0], X_OK) != 0)
+      redhat_shutdown = FALSE;
+
+      g_free (s);
+      g_free (t);
+    }
+
+  if (gdmtalk_shutdown  ||  redhat_shutdown)
     {
       GtkWidget *title, *spacer;
       GtkWidget *action_vbox, *hbox;
@@ -430,20 +609,50 @@
       gtk_box_pack_start (GTK_BOX (hbox), action_vbox, TRUE, TRUE, 0);
       gtk_widget_show (action_vbox);
       
-      r = gtk_radio_button_new_with_mnemonic (NULL, _("_Log out"));
+      r = logout = gtk_radio_button_new_with_mnemonic (NULL, _("_Log Out"));
       gtk_box_pack_start (GTK_BOX (action_vbox), r, FALSE, FALSE, 0);
+      if (gdmtalk_shutdown)
+	 {
+	 g_signal_connect (G_OBJECT (r), "toggled",
+			   G_CALLBACK (set_exit_action),
+			   GINT_TO_POINTER (GDMT_GDMCMD_NONE));
+	   g_object_set (G_OBJECT (r), "sensitive", FALSE, NULL);
+      }
       gtk_widget_show (r);
 
       r = halt = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (r), _("Sh_ut down"));
       gtk_box_pack_start (GTK_BOX (action_vbox), r, FALSE, FALSE, 0);
+      if (gdmtalk_shutdown)
+	 {
+	 g_signal_connect (G_OBJECT (halt), "toggled",
+			   G_CALLBACK (set_exit_action),
+			   GINT_TO_POINTER (GDMT_GDMCMD_HALT));
+	   g_object_set (G_OBJECT (r), "sensitive", FALSE, NULL);
+      }
       gtk_widget_show (r);
 
       r = reboot = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (r), _("_Restart the computer"));
       gtk_box_pack_start (GTK_BOX (action_vbox), r, FALSE, FALSE, 0);
+      if (gdmtalk_shutdown)
+	 {
+	 g_signal_connect (G_OBJECT (reboot), "toggled",
+			   G_CALLBACK (set_exit_action),
+			   GINT_TO_POINTER (GDMT_GDMCMD_REBOOT));
+	   g_object_set (G_OBJECT (r), "sensitive", FALSE, NULL);
+      }
       gtk_widget_show (r);
+
+      if (gdmtalk_shutdown)
+      {
+	 r = suspend = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (r), _("Sus_pend the computer"));
+	 gtk_box_pack_start (GTK_BOX (action_vbox), r, FALSE, FALSE, 0);
+	 g_signal_connect (G_OBJECT (suspend), "toggled",
+			   G_CALLBACK (set_exit_action),
+			   GINT_TO_POINTER (GDMT_GDMCMD_SUSPEND));
+	   g_object_set (G_OBJECT (r), "sensitive", FALSE, NULL);
+	   gtk_widget_show (r);
+	 }
     }
-  g_free (s);
-  g_free (t);
 
   gsm_center_window_on_screen (GTK_WINDOW (box), screen, monitor);
 
@@ -472,11 +681,22 @@
 
   response = gtk_dialog_run (GTK_DIALOG (box));
 
-  if (halt)
-    halt_active = GTK_TOGGLE_BUTTON (halt)->active;
+  if (gdmt_ct) {
+    gdmt_shutdown (gdmt_ct);
+    gdmt_unref (gdmt_ct);
+    gdmt_ct = NULL;
+  }
 
-  if (reboot)
-    reboot_active = GTK_TOGGLE_BUTTON (reboot)->active;
+  if (gdmtalk_shutdown)
+    redhat_shutdown = FALSE;
+  else if (redhat_shutdown)
+    {
+      if (halt)
+	 halt_active = GTK_TOGGLE_BUTTON (halt)->active;
+
+      if (reboot)
+	 reboot_active = GTK_TOGGLE_BUTTON (reboot)->active;
+    }
 
   if (toggle_button)
     save_active = GTK_TOGGLE_BUTTON (toggle_button)->active;

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil


Reply to: