diff options
author | Felix Bellaby <felix@src.gnome.org> | 1999-03-22 21:34:50 +0000 |
---|---|---|
committer | Felix Bellaby <felix@src.gnome.org> | 1999-03-22 21:34:50 +0000 |
commit | 7ea7c20d0543551ed867f8ebb8868a2249667d68 (patch) | |
tree | 87538d79c85b06233a30a335d67a8b6582ad2069 | |
parent | 2c3d3dc2fe09b49afaf26f3dd601575c5fb4181b (diff) | |
download | gnome-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/ChangeLog | 14 | ||||
-rw-r--r-- | gnome-session/command.c | 85 | ||||
-rw-r--r-- | gnome-session/default.in | 5 | ||||
-rw-r--r-- | gnome-session/gsm-client-row.c | 16 | ||||
-rw-r--r-- | gnome-session/gsm-protocol.c | 60 | ||||
-rw-r--r-- | gnome-session/gsm-protocol.h | 4 | ||||
-rw-r--r-- | gnome-session/ice.c | 7 | ||||
-rw-r--r-- | gnome-session/main.c | 4 | ||||
-rw-r--r-- | gnome-session/manager.c | 329 | ||||
-rw-r--r-- | gnome-session/manager.h | 24 | ||||
-rw-r--r-- | gnome-session/save.c | 1 | ||||
-rw-r--r-- | gnome-session/session-properties.c | 20 | ||||
-rw-r--r-- | gnome-session/session.h | 19 |
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, §ion, 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 |