summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Bellaby <felix@src.gnome.org>1999-03-22 21:34:50 +0000
committerFelix Bellaby <felix@src.gnome.org>1999-03-22 21:34:50 +0000
commit7ea7c20d0543551ed867f8ebb8868a2249667d68 (patch)
tree87538d79c85b06233a30a335d67a8b6582ad2069
parent2c3d3dc2fe09b49afaf26f3dd601575c5fb4181b (diff)
downloadgnome-session-7ea7c20d0543551ed867f8ebb8868a2249667d68.tar.gz
remove clients on failure to respond to XSMP commands with user
* manager.c (no_response_warning): remove clients on failure to respond to XSMP commands with user confirmation via separate GUI. (remove_client): add capacity to remove unresponsive clients. * session.h, command.c, save.c: add confirmations to warning dialogs. * main.c: add warn-delay option. * ice.c (accept_connection): accept during shutdown to return error. * manager.h: update to match above changes. * default.in: new session containing a client which shows warnings. * session-properties.c: support a warning only mode. * gsm-client-row.c: relocate warning handler code into parent: * gsm-protocol.c, gsm-protocol.h: obtain user confirmations.
-rw-r--r--gnome-session/ChangeLog14
-rw-r--r--gnome-session/command.c85
-rw-r--r--gnome-session/default.in5
-rw-r--r--gnome-session/gsm-client-row.c16
-rw-r--r--gnome-session/gsm-protocol.c60
-rw-r--r--gnome-session/gsm-protocol.h4
-rw-r--r--gnome-session/ice.c7
-rw-r--r--gnome-session/main.c4
-rw-r--r--gnome-session/manager.c329
-rw-r--r--gnome-session/manager.h24
-rw-r--r--gnome-session/save.c1
-rw-r--r--gnome-session/session-properties.c20
-rw-r--r--gnome-session/session.h19
13 files changed, 469 insertions, 119 deletions
diff --git a/gnome-session/ChangeLog b/gnome-session/ChangeLog
index 729f9a5f..38dd5c92 100644
--- a/gnome-session/ChangeLog
+++ b/gnome-session/ChangeLog
@@ -1,3 +1,17 @@
+1999-03-22 Felix Bellaby <felix@pooh.u-net.com>
+
+ * manager.c (no_response_warning): remove clients on failure to
+ respond to XSMP commands with user confirmation via separate GUI.
+ (remove_client): add capacity to remove unresponsive clients.
+ * session.h, command.c, save.c: add confirmations to warning dialogs.
+ * main.c: add warn-delay option.
+ * ice.c (accept_connection): accept during shutdown to return error.
+ * manager.h: update to match above changes.
+ * default.in: new session containing a client which shows warnings.
+ * session-properties.c: support a warning only mode.
+ * gsm-client-row.c: relocate warning handler code into parent:
+ * gsm-protocol.c, gsm-protocol.h: obtain user confirmations.
+
1999-03-19 Felix Bellaby <felix@pooh.u-net.com>
* main.c (main): extra command line options.
diff --git a/gnome-session/command.c b/gnome-session/command.c
index f15b7edc..2ba20401 100644
--- a/gnome-session/command.c
+++ b/gnome-session/command.c
@@ -35,6 +35,7 @@ static GHashTable* handle_table = NULL;
typedef enum
{
COMMAND_ACTIVE = 0,
+ COMMAND_HANDLE_WARNINGS,
COMMAND_INACTIVE,
} CommandState;
@@ -83,7 +84,7 @@ make_command (const gchar* command_name, const gchar* argument)
}
static SmProp*
-make_client_reasons (const gchar* client_handle,
+make_client_reasons (const gchar* client_handle, const gboolean confirm,
const guint count, gchar** reasons)
{
SmProp *prop = (SmProp*) malloc (sizeof (SmProp));
@@ -91,16 +92,18 @@ make_client_reasons (const gchar* client_handle,
prop->name = strdup (GsmClientEvent);
prop->type = strdup (SmLISTofARRAY8);
- prop->num_vals = 2 + count;
+ prop->num_vals = 3 + count;
prop->vals = (SmPropValue*) malloc (sizeof (SmPropValue) * prop->num_vals);
prop->vals[0].value = strdup (GsmReasons);
prop->vals[0].length = strlen (prop->vals[0].value);
- prop->vals[1].value = strdup(client_handle);
+ prop->vals[1].value = strdup (client_handle);
prop->vals[1].length = strlen (prop->vals[1].value);
+ prop->vals[2].value = strdup (confirm ? "1" : "0");
+ prop->vals[2].length = strlen (prop->vals[2].value);
for (i = 0; i < count; i++)
{
- prop->vals[i+2].value = strdup(reasons[i]);
- prop->vals[i+2].length = strlen (prop->vals[i+2].value);
+ prop->vals[i+3].value = strdup(reasons[i]);
+ prop->vals[i+3].length = strlen (prop->vals[i+3].value);
}
return prop;
@@ -124,19 +127,60 @@ prop_dup (const SmProp* old_prop)
return prop;
}
+Client* get_warner (void)
+{
+ GSList *list;
+
+ for (list = selector_list; list; list = list->next)
+ {
+ Client *client = (Client*)selector_list->data;
+ if (client->command_data->state == COMMAND_HANDLE_WARNINGS)
+ return client;
+ }
+ return NULL;
+}
+
void
-client_reasons (const gchar* client_handle, gint count, gchar** reasons)
+client_reasons (Client* client, gboolean confirm,
+ gint count, gchar** reasons)
{
- if (count > 0 && client_handle)
+ if (count > 0 && client->connection && !client->warning)
{
- GSList *list;
- for (list = selector_list; list; list = list->next)
+ Client *warner = get_warner ();
+
+ if (warner)
{
- Client *client = (Client*)list->data;
- GSList* prop_list = NULL;
- APPEND (prop_list, make_client_reasons (client_handle,
+ GSList* prop_list = NULL;
+
+ if (!client->handle)
+ {
+ SmProp* prop;
+ /* client is making a mess of its very first save ...
+ * have to tell the warner who the client is first: */
+ client->match_rule = MATCH_WARN;
+ client->handle = command_handle_new (client);
+ APPEND (prop_list, make_client_event (client->handle, GsmSave));
+ send_properties (warner, prop_list);
+ prop_list = NULL;
+ if ((prop = find_property_by_name (client, SmRestartCommand)))
+ {
+ APPEND (prop_list, make_client_event (client->handle,
+ GsmProperty));
+ APPEND (prop_list, prop_dup (prop));
+ send_properties (warner, prop_list);
+ prop_list = NULL;
+ }
+ }
+
+ APPEND (prop_list, make_client_reasons (client->handle, confirm,
count, reasons));
- send_properties (client, prop_list);
+ send_properties (warner, prop_list);
+ client->warning = TRUE;
+ }
+ else if (confirm)
+ {
+ /* This is a failsafe that should never be needed */
+ remove_client (client);
}
}
}
@@ -273,6 +317,11 @@ command (Client* client, int nprops, SmProp** props)
APPEND (selector_list, client);
else if (!strcmp (prop->vals[0].value, GsmDeselectClientEvents))
REMOVE (selector_list, client);
+ else if (!strcmp (prop->vals[0].value, GsmHandleWarnings))
+ {
+ if (g_slist_find (selector_list, client))
+ client->command_data->state = COMMAND_HANDLE_WARNINGS;
+ }
else if (!strcmp (prop->vals[0].value, GsmGetLastSession))
{
GSList* prop_list = NULL;
@@ -305,7 +354,8 @@ command (Client* client, int nprops, SmProp** props)
while ((iter = gnome_config_iterator_next(iter, &section, NULL)))
{
- if (strcasecmp (section, CHOOSER_SESSION))
+ if (strcasecmp (section, CHOOSER_SESSION) &&
+ strcasecmp (section, WARNER_SESSION))
{
SmProp* prop = make_command (GsmReadSession, section);
@@ -475,6 +525,13 @@ command (Client* client, int nprops, SmProp** props)
}
}
}
+ else if (arg && !strcmp (prop->vals[0].value, GsmClearClientWarning))
+ {
+ Client* client1 = (Client*) command_handle_lookup (arg);
+
+ if (client1)
+ client1->warning = FALSE;
+ }
else if (arg && !strcmp (prop->vals[0].value, GsmChangeProperties))
{
Client* client1 = (Client*) command_handle_lookup (arg);
diff --git a/gnome-session/default.in b/gnome-session/default.in
index 1865b25d..444bf2e8 100644
--- a/gnome-session/default.in
+++ b/gnome-session/default.in
@@ -44,4 +44,9 @@ num_clients=11
[Chooser]
0,id=chooser0
0,RestartCommand=session-properties-capplet --init-session-settings --sm-client-id chooser0
+num_clients=1
+
+[Warner]
+0,id=warner0
+0,RestartCommand=session-properties-capplet --init-session-settings --warner --sm-client-id warner0
num_clients=1 \ No newline at end of file
diff --git a/gnome-session/gsm-client-row.c b/gnome-session/gsm-client-row.c
index 75a22b01..1c00540e 100644
--- a/gnome-session/gsm-client-row.c
+++ b/gnome-session/gsm-client-row.c
@@ -71,7 +71,6 @@ static GsmClientClass *parent_class = NULL;
static void gsm_client_row_destroy (GtkObject *o);
static void client_remove (GsmClient* client);
-static void client_reasons (GsmClient* client, GSList* reasons);
static void client_command (GsmClient* client, gchar* command);
static void client_state (GsmClient* client, GsmState state);
static void client_style (GsmClient* client, GsmStyle style);
@@ -90,7 +89,6 @@ gsm_client_row_class_init (GsmClientRowClass *klass)
client_class->command = client_command;
client_class->style = client_style;
client_class->order = client_order;
- client_class->reasons = client_reasons;
create_stock_menu_pixmaps ();
@@ -331,20 +329,6 @@ client_style (GsmClient* client, GsmStyle style)
gsm_client_row_set_style ((GsmClientRow*)client, style);
}
-/* FIXME: this could almost certainly be improved ... */
-static void
-client_reasons (GsmClient* client, GSList* reasons)
-{
- GtkWidget* dialog;
- gchar* message = client->command;
-
- for (;reasons; reasons = reasons->next)
- message = g_strjoin ("\n", message, (gchar*)reasons->data, NULL);
-
- dialog = gnome_warning_dialog (message);
- g_free (message);
-}
-
static void
create_stock_menu_pixmaps (void)
{
diff --git a/gnome-session/gsm-protocol.c b/gnome-session/gsm-protocol.c
index 813d6689..489d4ba2 100644
--- a/gnome-session/gsm-protocol.c
+++ b/gnome-session/gsm-protocol.c
@@ -204,6 +204,9 @@ gsm_session_live (GsmClientFactory client_factory, gpointer data)
GsmSelectClientEvents, NULL), NULL);
command (the_protocol,
gsm_args_to_prop (GsmCommand,
+ GsmHandleWarnings, NULL), NULL);
+ command (the_protocol,
+ gsm_args_to_prop (GsmCommand,
GsmListClients, NULL), NULL);
}
return GTK_OBJECT (session);
@@ -288,6 +291,8 @@ enum {
};
static gint gsm_client_signals[NSIGNALS];
+static void client_reasons (GsmClient* client, gboolean confirm,
+ GSList* reasons);
static void
gsm_client_class_init (GsmClientClass *klass)
@@ -306,11 +311,11 @@ gsm_client_class_init (GsmClientClass *klass)
GTK_TYPE_NONE, 0);
gsm_client_signals[REASONS] =
gtk_signal_new ("reasons",
- GTK_RUN_LAST,
+ GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (GsmClientClass, reasons),
- gtk_marshal_NONE__POINTER,
- GTK_TYPE_NONE, 1, GTK_TYPE_POINTER);
+ gtk_marshal_NONE__INT_POINTER,
+ GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_POINTER);
gsm_client_signals[COMMAND] =
gtk_signal_new ("command",
GTK_RUN_LAST,
@@ -345,7 +350,7 @@ gsm_client_class_init (GsmClientClass *klass)
object_class->destroy = gsm_client_destroy;
klass->remove = NULL;
- klass->reasons = NULL;
+ klass->reasons = client_reasons;
klass->command = NULL;
klass->state = NULL;
klass->style = NULL;
@@ -481,6 +486,40 @@ gsm_client_commit_order (GsmClient *client)
NULL);
}
+static void
+client_reasons (GsmClient* client, gboolean confirm, GSList* reasons)
+{
+ GtkWidget* dialog;
+ gchar* message = g_strjoin ("\n", client->command, "", NULL);
+
+ for (;reasons; reasons = reasons->next)
+ message = g_strjoin ("\n", message, (gchar*)reasons->data, NULL);
+
+ /* Hmm, may need to be override redirect as well since WMs are quite
+ likely to be the source of the errors... */
+ //dialog = gnome_warning_dialog (message);
+ dialog = gnome_message_box_new (message, GNOME_MESSAGE_BOX_WARNING, NULL);
+ gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (dialog),
+ _("Remove Program"),
+ GNOME_STOCK_PIXMAP_TRASH);
+ if (confirm)
+ {
+ gnome_dialog_append_button (GNOME_DIALOG (dialog),
+ GNOME_STOCK_BUTTON_CANCEL);
+ gnome_dialog_set_default (GNOME_DIALOG (dialog), 1);
+ gnome_dialog_button_connect_object (GNOME_DIALOG (dialog), 0,
+ GTK_SIGNAL_FUNC (gsm_client_commit_remove),
+ GTK_OBJECT (client));
+ }
+ gtk_window_set_position ((GtkWindow *) dialog, GTK_WIN_POS_CENTER);
+ gtk_window_set_modal ((GtkWindow *) (dialog), TRUE);
+ gtk_widget_show_all (dialog);
+ gnome_win_hints_set_state((GtkWidget *) dialog, WIN_STATE_FIXED_POSITION);
+ gnome_win_hints_set_layer((GtkWidget *) dialog, WIN_LAYER_ABOVE_DOCK);
+ gnome_dialog_run ((GnomeDialog *) (dialog));
+ g_free (message);
+}
+
/* GSM_PROTOCOL object */
static void gsm_protocol_destroy (GtkObject *o);
@@ -694,7 +733,7 @@ dispatch_event (SmcConn smc_conn, SmPointer data,
{
gint i;
gchar *restart_command = NULL, *command = NULL;
- GsmClient* client = find_client (props[0], data);
+ GsmClient* client = find_client (props[0], live_session);
GsmSession* session = client->session;
prop_free (props[0]);
@@ -764,11 +803,20 @@ dispatch_event (SmcConn smc_conn, SmPointer data,
if (!strcmp (type, GsmReasons))
{
GSList *list = gsm_prop_to_list (props[i]);
+ gboolean confirm;
list = g_slist_remove (list, list->data);
list = g_slist_remove (list, list->data);
+ confirm = strcmp (list->data, "0");
+ list = g_slist_remove (list, list->data);
gtk_signal_emit (GTK_OBJECT (client),
- gsm_client_signals[REASONS], list);
+ gsm_client_signals[REASONS],
+ confirm, list);
+ command (protocol,
+ gsm_args_to_prop (GsmCommand,
+ GsmClearClientWarning,
+ client->handle, NULL),
+ NULL);
g_slist_free (list);
}
else if (!strcmp (type, GsmRemove))
diff --git a/gnome-session/gsm-protocol.h b/gnome-session/gsm-protocol.h
index 27bf0083..44baceb9 100644
--- a/gnome-session/gsm-protocol.h
+++ b/gnome-session/gsm-protocol.h
@@ -162,8 +162,8 @@ struct _GsmClientClass {
void (* state) (GsmClient *client, GsmState new_state);
void (* command) (GsmClient *client, gchar* new_command);
/* Signal emitted when gnome-session encounters an client error:
- * the list of strings argument is freed internally. */
- void (* reasons) (GsmClient *client, GSList* reasons);
+ * Confirm is TRUE when the client must be removed explicitly. */
+ void (* reasons) (GsmClient *client, gboolean confirm, GSList* reasons);
};
guint gsm_client_get_type (void);
diff --git a/gnome-session/ice.c b/gnome-session/ice.c
index d37fc7b1..8315884d 100644
--- a/gnome-session/ice.c
+++ b/gnome-session/ice.c
@@ -55,8 +55,8 @@ static IceAuthFileEntry* file_entry_new (const gchar* protocol,
static GSList* read_authfile(gchar* filename);
static void write_authfile(gchar* filename, GSList* entries);
-/* This is called when a client tries to connect. We accept most
- connections. We reject connections while doing a shutdown. */
+/* This is called when a client tries to connect.
+ * We still accept connections during a shutdown so we can handle warnings. */
static void
accept_connection (gpointer client_data, gint source,
GdkInputCondition conditon)
@@ -65,9 +65,6 @@ accept_connection (gpointer client_data, gint source,
IceAcceptStatus status;
IceConnectStatus status2 = IceConnectPending;
- if (shutdown_in_progress_p ())
- return;
-
connection = IceAcceptConnection ((IceListenObj) client_data, &status);
if (status != IceAcceptSuccess)
return;
diff --git a/gnome-session/main.c b/gnome-session/main.c
index d57e3317..c443a874 100644
--- a/gnome-session/main.c
+++ b/gnome-session/main.c
@@ -47,7 +47,7 @@ gboolean trashing = FALSE;
guint purge_delay = 30000;
/* Wait period for clients to save. */
-guint save_delay = 30000;
+guint warn_delay = 5000;
/* Wait period for clients to die during shutdown. */
guint suicide_delay = 10000;
@@ -57,7 +57,7 @@ static const struct poptOption options[] = {
{"trash-saves", '\0', POPT_ARG_NONE, &trashing, 0, N_("Disable normal operation by saving into the Trash session"), NULL},
{"failsafe", '\0', POPT_ARG_NONE, &failsafe, 0, N_("Only read saved sessions from the default.session file"), NULL},
{"purge-delay", '\0', POPT_ARG_INT, &purge_delay, 0, N_("Millisecond period spent waiting for clients to register (0=forever)"), NULL},
- {"save-delay", '\0', POPT_ARG_INT, &save_delay, 0, N_("Millisecond period spent waiting for all clients to save (0=forever)"), NULL},
+ {"warn-delay", '\0', POPT_ARG_INT, &warn_delay, 0, N_("Millisecond period spent waiting for clients to respond (0=forever)"), NULL},
{"suicide-delay", '\0', POPT_ARG_INT, &suicide_delay, 0, N_("Millisecond period spent waiting for clients to die (0=forever)"), NULL},
{NULL, '\0', 0, NULL, 0}
};
diff --git a/gnome-session/manager.c b/gnome-session/manager.c
index 0c675c46..293c7192 100644
--- a/gnome-session/manager.c
+++ b/gnome-session/manager.c
@@ -40,6 +40,7 @@ static enum {
SAVE_PHASE_2,
SAVE_CANCELLED,
STARTING_SESSION,
+ STARTING_WARNER,
SHUTDOWN
} save_state;
@@ -61,6 +62,12 @@ GSList *purge_retain_list = NULL;
/* List of all live clients in the default state. */
GSList *live_list = NULL;
+/* Timeout for clients that are slow to respond to SmSaveYourselfRequest */
+gint warn_timeout_id = -1;
+
+/* Used to monitor programs that claim to be interacting. */
+gboolean interact_ping_replied = TRUE;
+
/* A queued save yourself request */
typedef struct _SaveRequest
{
@@ -78,6 +85,9 @@ static GSList *save_request_list = NULL;
/* This is true if a shutdown is expected to follow the current save. */
static int shutting_down = 0;
+/* This is true if we are starting a client to print warnings. */
+static int starting_warner = 0;
+
/* List of all clients waiting for the interaction token. The head of
the list actually has the token. */
static GSList *interact_list = NULL;
@@ -118,6 +128,9 @@ static gint compare_interact_request (gconstpointer a, gconstpointer b);
typedef void message_func (SmsConn connection);
static void
+close_connection (SmsConn connection, SmPointer data, int count,
+ char **reasons);
+static void
save_yourself_request (SmsConn connection, SmPointer data, int save_type,
Bool shutdown, int interact_style, Bool fast,
Bool global);
@@ -256,7 +269,7 @@ free_client (Client *client)
/* Run a command on a client. */
gint
-run_command (const Client* client, const gchar* command)
+run_command (Client* client, const gchar* command)
{
int argc, envc, envpc, pid = -1;
gboolean def, envd;
@@ -289,10 +302,13 @@ run_command (const Client* client, const gchar* command)
if (errno)
{
- gchar *message;
- message = g_strconcat (argv[0], " : ", g_strerror(errno), NULL);
- client_reasons (client->handle, 1, &message);
- g_free (message);
+ if (strcmp (command, SmRestartCommand) || client->connection)
+ {
+ gchar *message;
+ message = g_strconcat (argv[0], " : ", g_strerror(errno), NULL);
+ client_reasons (client, FALSE, 1, &message);
+ g_free (message);
+ }
pid = -1;
errno = 0;
}
@@ -356,7 +372,7 @@ process_load_request (GSList *client_list)
if (bad_match)
{
gchar *message = _("Wait abandoned due to conflict.");
- client_reasons (client->handle, 1, &message);
+ client_reasons (client, FALSE, 1, &message);
client_event (client->handle, GsmRemove);
REMOVE (purge_drop_list, client);
free_client (client);
@@ -467,17 +483,66 @@ gint
remove_client (Client* client)
{
GSList* list;
- /* This only uses the ammunition provided by the protocol to kill clients.
- * The user must resort to kill or xkill to destroy badly broken clients
- * and can place these in the SmShutdownCommand to facilitate logouts. */
- if (save_state != MANAGER_IDLE && save_state != STARTING_SESSION)
+ if (g_slist_find (interact_list, client))
return -1;
-
+
if (g_slist_find (live_list, client))
{
+ if (save_state != SHUTDOWN)
+ {
+ set_client_restart_style (client, SmRestartNever);
+ SmsDie (client->connection);
+ }
+ else
+ {
+ IceConn ice_conn = SmsGetIceConnection (client->connection);
+
+ IceSetShutdownNegotiation (ice_conn, False);
+ IceCloseConnection (ice_conn);
+ REMOVE (live_list, client);
+ client_event (client->handle, GsmRemove);
+ update_save_state ();
+ }
+ return 1;
+ }
+
+ for (list = save_request_list; list;)
+ {
+ SaveRequest *request = (SaveRequest*)list->data;
+
+ list = list->next;
+ if (request->client == client) {
+ g_free(request);
+ REMOVE (save_request_list, request);
+ }
+ }
+
+ if (g_slist_find (save_yourself_list, client))
+ {
+ IceConn ice_conn = SmsGetIceConnection (client->connection);
+
set_client_restart_style (client, SmRestartNever);
SmsDie (client->connection);
+ IceSetShutdownNegotiation (ice_conn, False);
+ IceCloseConnection (ice_conn);
+ REMOVE (save_yourself_list, client);
+ client_event (client->handle, GsmRemove);
+ update_save_state ();
+ return 1;
+ }
+
+ if (g_slist_find (save_yourself_p2_list, client))
+ {
+ IceConn ice_conn = SmsGetIceConnection (client->connection);
+
+ set_client_restart_style (client, SmRestartNever);
+ SmsDie (client->connection);
+ IceSetShutdownNegotiation (ice_conn, False);
+ IceCloseConnection (ice_conn);
+ REMOVE (save_yourself_p2_list, client);
+ client_event (client->handle, GsmRemove);
+ update_save_state ();
return 1;
}
@@ -678,12 +743,135 @@ interact_request (SmsConn connection, SmPointer data, int dialog_type)
if (send)
{
save_state = SENDING_INTERACT;
+ interact_ping_replied = TRUE;
SmsInteract (connection);
save_state = SAVE_PHASE_1;
}
}
}
+static void
+interact_ping_reply (IceConn ice_conn, IcePointer data)
+{
+ if (interact_list &&
+ interact_list->data == data)
+ interact_ping_replied = TRUE;
+}
+
+/* Used to tell GUI when things are going slowly. The GUI may intervene
+ * by removing the clients from the session. Clients that fail to respond
+ * to a DIe are always removed. */
+static gint
+no_response_warning (gpointer data)
+{
+ GSList *list;
+ gchar *message;
+ gchar *reasons[3];
+ Client* warner = get_warner ();
+
+ if (!warner)
+ {
+ if (!starting_warner)
+ {
+ Session *session;
+ gboolean old_failsafe = failsafe;
+ failsafe = TRUE;
+ session = read_session (WARNER_SESSION);
+ failsafe = old_failsafe;
+ warner = (Client*)session->client_list->data;
+ start_client(warner);
+ g_slist_free (session->client_list);
+ session->client_list = NULL;
+ free_session (session);
+ }
+ starting_warner++;
+ if (starting_warner < 5)
+ return 1;
+ }
+ starting_warner = 0;
+
+ switch (save_state)
+ {
+ case SAVE_CANCELLED:
+ message = "ShutdownCancelled";
+ break;
+
+ case SAVE_PHASE_2:
+ message = "SaveYourselfPhase2";
+ break;
+
+ case SAVE_PHASE_1:
+ message = interact_list ? "Interact" : "SaveYourself";
+ break;
+
+ case SHUTDOWN:
+ message = "Die";
+ break;
+
+ default:
+ /* not possible */
+ return 0;
+ }
+
+ reasons[0] = g_strdup_printf(_("No response to the %s command."), message);
+ reasons[1] = _("The program may be slow, stopped or broken.");
+ reasons[2] = _("You may wait for it to respond or remove it.");
+
+ if (interact_list)
+ {
+ Client *client = (Client *)interact_list->data;
+
+ if (interact_ping_replied)
+ {
+ IceConn ice_conn = SmsGetIceConnection (client->connection);
+ interact_ping_replied = FALSE;
+ IcePing (ice_conn, interact_ping_reply, (IcePointer)client);
+ }
+ else
+ {
+ client_reasons (client, TRUE, 3, reasons);
+ }
+ }
+ else
+ switch (save_state)
+ {
+ case SAVE_CANCELLED:
+ case SAVE_PHASE_2:
+ for (list = save_yourself_p2_list; list; )
+ {
+ Client* client = (Client *) list->data;
+ list = list->next;
+ client_reasons (client, TRUE, 3, reasons);
+ }
+ /* fall through */
+
+ case SAVE_PHASE_1:
+ for (list = save_yourself_list; list; )
+ {
+ Client* client = (Client *) list->data;
+ list = list->next;
+ client_reasons (client, TRUE, 3, reasons);
+ }
+ break;
+
+ case SHUTDOWN:
+ for (list = live_list; list; )
+ {
+ Client* client = (Client *) list->data;
+ list = list->next;
+ if (client != warner)
+ client_reasons (client, TRUE, 3, reasons);
+ }
+ break;
+
+ default:
+ /* not possible */
+ break;
+ }
+ g_free (reasons[0]);
+ return 1;
+}
+
static void
interact_done (SmsConn connection, SmPointer data, Bool cancel)
{
@@ -716,6 +904,7 @@ interact_done (SmsConn connection, SmPointer data, Bool cancel)
{
save_state = SENDING_INTERACT;
client = (Client *) interact_list->data;
+ interact_ping_replied = TRUE;
SmsInteract (client->connection);
save_state = SAVE_PHASE_1;
}
@@ -750,8 +939,15 @@ process_save_request (Client* client, int save_type, gboolean shutdown,
SmsSaveYourself (client->connection,
save_type, 0, interact_style, fast);
}
- if (shutting_down || save_yourself_list)
- save_state = SAVE_PHASE_1;
+ if (shutting_down || save_yourself_list)
+ {
+ /* Give apps extra time on first save as they may call SmcOpenConnection
+ * long before entering the select on the resulting connection. */
+ gint delay = client->handle ? warn_delay : purge_delay;
+ save_state = SAVE_PHASE_1;
+ if (delay)
+ warn_timeout_id = gtk_timeout_add (delay, no_response_warning, NULL);
+ }
else
save_state = MANAGER_IDLE;
@@ -807,23 +1003,6 @@ run_shutdown_commands (const GSList *list)
}
}
-/* Used to exit */
-static gint
-suicide (gpointer data)
-{
- while (live_list)
- {
- Client *client = (Client*)live_list->data;
-
- client_event (client->handle, GsmRemove);
- REMOVE (live_list, client);
- g_warning ("Client %s failed to respond to the Die command", client->id);
- free_client (client);
- }
- gtk_main_quit ();
- return 0;
-}
-
/* This is a helper function which makes sure that the save_state
variable is correctly set after a change to the save_yourself lists. */
static void
@@ -869,6 +1048,12 @@ update_save_state ()
{
save_state = SENDING_MESSAGES;
send_message (&save_yourself_p2_list, SmsSaveYourselfPhase2);
+
+ if (warn_timeout_id > -1)
+ gtk_timeout_remove (warn_timeout_id);
+ if (warn_delay)
+ warn_timeout_id = gtk_timeout_add (warn_delay,
+ no_response_warning, NULL);
}
save_state = SAVE_PHASE_2;
}
@@ -878,10 +1063,17 @@ update_save_state ()
if (save_yourself_p2_list)
return;
+ if (warn_timeout_id > -1)
+ gtk_timeout_remove (warn_timeout_id);
+ warn_timeout_id = -1;
+
CONCAT (live_list, save_finished_list);
if (shutting_down)
{
+ Client *warner = get_warner();
+ GSList* list;
+
while (purge_drop_list)
{
Client *client = (Client*)purge_drop_list->data;
@@ -890,19 +1082,16 @@ update_save_state ()
REMOVE (purge_drop_list, client);
free_client (client);
}
- }
-
- write_session ();
+ write_session ();
- save_state = SENDING_MESSAGES;
-
- send_message (&save_finished_list,
- shutting_down ? SmsDie : SmsSaveComplete);
- save_finished_list = NULL;
- save_state = MANAGER_IDLE;
-
- if (shutting_down)
- {
+ for (list = save_finished_list; list;)
+ {
+ Client *client = (Client *)list->data;
+ list = list->next;
+ if (client != warner)
+ SmsDie (client->connection);
+ }
+ save_finished_list = NULL;
save_state = SHUTDOWN;
/* Run each shutdown command. These commands are only strictly
* needed by zombie clients. */
@@ -910,16 +1099,30 @@ update_save_state ()
run_shutdown_commands (live_list);
if (suicide_delay)
- gtk_timeout_add (suicide_delay, suicide, NULL);
+ warn_timeout_id = gtk_timeout_add (suicide_delay,
+ no_response_warning, NULL);
+ }
+ else
+ {
+ write_session ();
+
+ save_state = SENDING_MESSAGES;
+ send_message (&save_finished_list, SmsSaveComplete);
+ save_finished_list = NULL;
+ save_state = MANAGER_IDLE;
}
}
if (save_state == SHUTDOWN)
{
- if (live_list)
- return;
-
- suicide (NULL);
+ if (! live_list)
+ gtk_main_quit ();
+ else if (!live_list->next)
+ {
+ Client* client = (Client *) live_list->data;
+ if (client == get_warner())
+ SmsDie (client->connection);
+ }
}
if (save_state == SAVE_CANCELLED)
@@ -927,6 +1130,10 @@ update_save_state ()
if (save_yourself_list || save_yourself_p2_list)
return;
+ if (warn_timeout_id > -1)
+ gtk_timeout_remove (warn_timeout_id);
+ warn_timeout_id = -1;
+
CONCAT (live_list, save_finished_list);
write_session ();
@@ -971,11 +1178,19 @@ save_yourself_done (SmsConn connection, SmPointer data, Bool success)
(g_slist_find (save_yourself_list, client) == NULL &&
g_slist_find (save_yourself_p2_list, client) == NULL))
{
- /* A SmSaveYourselfDone from a broken client.
- FIXME: Should we inform the user ? */
+ /* The use of timeouts in the save process introduces the possibility
+ * that these are messages which the user was too impatient to wait
+ * for ... the clients concerned should have received die messages... */
return;
}
+ if (client->match_rule == MATCH_WARN)
+ {
+ client_event (client->handle, GsmRemove);
+ command_handle_free (client->handle);
+ client->handle = NULL;
+ }
+
/* Must delay assignment of handle for gsm protocol extensions until
* the initial save is complete on external clients so that we can
* match them against MATCH_PROP clients using properties. */
@@ -1067,7 +1282,7 @@ close_connection (SmsConn connection, SmPointer data, int count,
Client *client = (Client *) data;
IceConn ice_conn = SmsGetIceConnection (connection);
- client_reasons (client->handle, count, reasons);
+ client_reasons (client, FALSE, count, reasons);
SmFreeReasons (count, reasons);
IceSetShutdownNegotiation (ice_conn, False);
@@ -1128,7 +1343,8 @@ client_clean_up (Client* client)
else
{
gchar *message = _("Respawn abandoned due to failures.");
- client_reasons (client->handle, 1, &message);
+ /* This is a candidate for a confirm = TRUE. */
+ client_reasons (client, FALSE, 1, &message);
client_event (client->handle, GsmRemove);
free_client (client);
}
@@ -1153,6 +1369,7 @@ client_clean_up (Client* client)
if (interact_list)
{
client = (Client *) interact_list->data;
+ interact_ping_replied = TRUE;
SmsInteract (client->connection);
}
save_state = SAVE_PHASE_1;
@@ -1291,7 +1508,7 @@ new_client (SmsConn connection, SmPointer data, unsigned long *maskp,
{
Client *client;
- if (shutting_down)
+ if (shutting_down && !starting_warner)
{
*reasons = strdup (_("A session shutdown is in progress."));
return 0;
@@ -1302,6 +1519,7 @@ new_client (SmsConn connection, SmPointer data, unsigned long *maskp,
client->connection = connection;
client->attempts = 1;
client->connect_time = time (NULL);
+ client->warning = FALSE;
ice_set_clean_up_handler (SmsGetIceConnection (connection),
(GDestroyNotify)client_clean_up, (gpointer)client);
@@ -1375,10 +1593,3 @@ save_session (int save_type, gboolean shutdown, int interact_style,
process_save_request (NULL, save_type, shutdown,
interact_style, fast, TRUE);
}
-
-int
-shutdown_in_progress_p (void)
-{
- return save_state != MANAGER_IDLE && shutting_down;
-}
-
diff --git a/gnome-session/manager.h b/gnome-session/manager.h
index 803e8857..de424ec2 100644
--- a/gnome-session/manager.h
+++ b/gnome-session/manager.h
@@ -38,6 +38,9 @@
#define CHOOSER_SESSION "Chooser"
/* Trash session name. */
+#define WARNER_SESSION "Warner"
+
+/* Trash session name. */
#define TRASH_SESSION "Trash"
/* Config section used for gnome-session's own config details. */
@@ -61,7 +64,8 @@ typedef enum {
MATCH_ID,
MATCH_FAKE_ID,
MATCH_DONE,
- MATCH_PROP
+ MATCH_PROP,
+ MATCH_WARN
} MatchRule;
/* Additional details for clients that speak our command protocol */
@@ -95,6 +99,9 @@ typedef struct
/* Used to avoid registering clients with ids from default.session */
MatchRule match_rule;
+ /* Used to suspend further warnings while user is responding to a warning */
+ gboolean warning;
+
/* Used to decouple SmsGetPropertiesProc and SmsReturnProperties
* for the purpose of extending the protocol: */
GSList* get_prop_replies;
@@ -124,7 +131,7 @@ extern guint purge_delay;
extern guint suicide_delay;
/* Milliseconds to wait for clients to complete save before reporting error. */
-extern guint save_delay;
+extern guint warn_delay;
/* Waiting for the chooser to tell us which session to start. */
extern gboolean choosing;
@@ -155,18 +162,13 @@ void free_client (Client* client);
/* Run the Discard, Resign or Shutdown command on a client.
* Returns the pid or -1 if unsuccessful. */
-gint run_command (const Client* client, const gchar* command);
+gint run_command (Client* client, const gchar* command);
/* Call this to initiate a session save, and perhaps a shutdown.
Save requests are queued internally. */
void save_session (int save_type, gboolean shutdown, int interact_style,
gboolean fast);
-/* Returns true if shutdown in progress, false otherwise. Note it is
- possible for this function to return true and then later return
- false -- the shutdown might be cancelled. */
-int shutdown_in_progress_p (void);
-
/* This is called via ICE when a new client first connects. */
Status new_client (SmsConn connection, SmPointer data, unsigned long *maskp,
SmsCallbacks *callbacks, char **reasons);
@@ -265,7 +267,11 @@ void client_event (const gchar* handle, const gchar* event);
void client_property (const gchar* handle, int nprops, SmProp** props);
/* Log reasons for an event with the client event selectors. */
-void client_reasons (const gchar* handle, gint count, gchar** reasons);
+void client_reasons (Client* client, gboolean confirm,
+ gint count, gchar** reasons);
+
+/* Returns the client that prints up warnings for gnome-session (if any). */
+Client* get_warner (void);
/* Process a _GSM_Command protocol message. */
void command (Client* client, int nprops, SmProp** props);
diff --git a/gnome-session/save.c b/gnome-session/save.c
index cd85776a..56d1f9ed 100644
--- a/gnome-session/save.c
+++ b/gnome-session/save.c
@@ -256,6 +256,7 @@ read_one_client (Client *client)
client->properties = NULL;
client->priority = 50;
client->handle = command_handle_new ((gpointer)client);
+ client->warning = FALSE;
client->get_prop_replies = NULL;
client->get_prop_requests = 0;
client->command_data = NULL;
diff --git a/gnome-session/session-properties.c b/gnome-session/session-properties.c
index 875c761f..72a0e092 100644
--- a/gnome-session/session-properties.c
+++ b/gnome-session/session-properties.c
@@ -337,13 +337,23 @@ create_dialog (void)
return app;
}
+static gboolean init_settings = FALSE;
+static gboolean warner = FALSE;
+
+static const struct poptOption options[] = {
+ {"init-session-settings", '\0', POPT_ARG_NONE, &init_settings, 0, N_("Initialize session settings"), NULL},
+ {"warner", '\0', POPT_ARG_NONE, &warner, 0, N_("Only display warnings."), NULL},
+ {NULL, '\0', 0, NULL, 0}
+};
+
int
main (int argc, char *argv[])
{
bindtextdomain (PACKAGE, GNOMELOCALEDIR);
textdomain (PACKAGE);
- gnome_init ("session-properties", VERSION, argc, argv);
+ gnome_init_with_popt_table ("session-properties", VERSION, argc, argv,
+ options, 0, NULL);
gtk_signal_connect (GTK_OBJECT (gnome_master_client ()), "die",
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
@@ -354,10 +364,12 @@ main (int argc, char *argv[])
g_warning ("Could not connect to gnome-session.");
exit (1);
}
- if (argc == 1)
- create_app ();
- else
+ if (warner)
+ gsm_session_live (gsm_client_new, NULL);
+ else if (init_settings)
create_dialog ();
+ else
+ create_app ();
gtk_main ();
diff --git a/gnome-session/session.h b/gnome-session/session.h
index 898655d9..21d73072 100644
--- a/gnome-session/session.h
+++ b/gnome-session/session.h
@@ -54,7 +54,10 @@
* properties for the client with the specified handle */
#define GsmReasons "Reasons" /* explanation for next event */
/* These are sent when gnome-session has information about why a client
- * is being removed from the session: val[2], ... give the reasons. */
+ * is being removed or might need to be removed from the session:
+ * val[2].value=confirm = "0" removed by gnome-session automatically.
+ * = "1" removed iff a GsmRemoveClient is sent in reply.
+ * val[3].value, val[4].value, ...=the reasons(array of strings). */
/* THE GSM COMMAND PROTOCOL:
* Call SmcSetProperty with the FIRST property having:
@@ -73,11 +76,17 @@
* its operation: */
#define GsmSelectClientEvents "SelectClientEvents"
-/* Selects events occuring to gsm */
+/* Selects events occuring to gsm clients */
#define GsmDeselectClientEvents "DeselectClientEvents"
/* Deselects events occuring to gsm clients */
+#define GsmHandleWarnings "HandleWarnings"
+/* This flags that you are able to handle the warning messages that
+ * gnome-session sometimes needs to generate when clients are unresponsive.
+ * gnome-session will rely on ONE of the clients that sets this flag to handle
+ * the warnings. Only send this command when your warning handler works!!! */
+
#define GsmListClients "ListClients"
/* Returns the clients that are currently running as an array of events.
* Each client is represented by the last event that it generated (ignoring
@@ -110,6 +119,12 @@
* or silent clients. Any SmDiscardCommand will be called when the session
* is next saved. */
+#define GsmClearClientWarning "ClearClientWarning"
+/* Instructs gnome-session that a GsmReasons event with confirm=TRUE has been
+ * displayed to the user and any action chosen by the user has been taken.
+ * gnome-session suspends further warnings on the client until this command
+ * has been received. */
+
#define GsmChangeProperties "ChangeProperties"
/* Changes some properties for the client with the given GsmClientEvent handle.
* The remaining properties in the SmcSetProperties call give the changed