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 [EMAIL PROTECTED] | ICQ# 74513189 | GPG-Keyid: 4743206C JID: [EMAIL PROTECTED] | 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 <[EMAIL PROTECTED]>
+ 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 <[EMAIL PROTECTED]>
+ 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;
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil

