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