2937a09ead
This attempts to align second-granularity wakeups across the system, reducing CPU wakeups a little. I considered all timeouts of duration 1 second or greater, and moved the non-timing-critical ones to g_timeout_add_seconds(). (Actually, I didn't find any that I felt were timing-critical)
829 lines
22 KiB
C
829 lines
22 KiB
C
/* client-xsmp.c
|
|
* Copyright (C) 2007 Novell, Inc.
|
|
*
|
|
* 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 of the
|
|
* License, 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
|
|
* Lesser 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "gsm-client-xsmp.h"
|
|
#include "gsm-session.h"
|
|
|
|
/* FIXME */
|
|
#define GsmDesktopFile "_Gsm_DesktopFile"
|
|
|
|
static gboolean client_iochannel_watch (GIOChannel *channel,
|
|
GIOCondition condition,
|
|
gpointer data);
|
|
static gboolean client_protocol_timeout (gpointer data);
|
|
|
|
static void set_description (GsmClientXSMP *xsmp);
|
|
|
|
static const char *xsmp_get_client_id (GsmClient *client);
|
|
static pid_t xsmp_get_pid (GsmClient *client);
|
|
static char *xsmp_get_desktop_file (GsmClient *client);
|
|
static char *xsmp_get_restart_command (GsmClient *client);
|
|
static char *xsmp_get_discard_command (GsmClient *client);
|
|
static gboolean xsmp_get_autorestart (GsmClient *client);
|
|
|
|
static void xsmp_finalize (GObject *object);
|
|
static void xsmp_restart (GsmClient *client,
|
|
GError **error);
|
|
static void xsmp_save_yourself (GsmClient *client,
|
|
gboolean save_state);
|
|
static void xsmp_save_yourself_phase2 (GsmClient *client);
|
|
static void xsmp_interact (GsmClient *client);
|
|
static void xsmp_shutdown_cancelled (GsmClient *client);
|
|
static void xsmp_die (GsmClient *client);
|
|
|
|
G_DEFINE_TYPE (GsmClientXSMP, gsm_client_xsmp, GSM_TYPE_CLIENT)
|
|
|
|
static void
|
|
gsm_client_xsmp_init (GsmClientXSMP *xsmp)
|
|
{
|
|
;
|
|
}
|
|
|
|
static void
|
|
gsm_client_xsmp_class_init (GsmClientXSMPClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
|
|
|
|
object_class->finalize = xsmp_finalize;
|
|
|
|
client_class->get_client_id = xsmp_get_client_id;
|
|
client_class->get_pid = xsmp_get_pid;
|
|
client_class->get_desktop_file = xsmp_get_desktop_file;
|
|
client_class->get_restart_command = xsmp_get_restart_command;
|
|
client_class->get_discard_command = xsmp_get_discard_command;
|
|
client_class->get_autorestart = xsmp_get_autorestart;
|
|
|
|
client_class->restart = xsmp_restart;
|
|
client_class->save_yourself = xsmp_save_yourself;
|
|
client_class->save_yourself_phase2 = xsmp_save_yourself_phase2;
|
|
client_class->interact = xsmp_interact;
|
|
client_class->shutdown_cancelled = xsmp_shutdown_cancelled;
|
|
client_class->die = xsmp_die;
|
|
}
|
|
|
|
GsmClientXSMP *
|
|
gsm_client_xsmp_new (IceConn ice_conn)
|
|
{
|
|
GsmClientXSMP *xsmp;
|
|
GIOChannel *channel;
|
|
int fd;
|
|
|
|
xsmp = g_object_new (GSM_TYPE_CLIENT_XSMP, NULL);
|
|
xsmp->props = g_ptr_array_new ();
|
|
|
|
xsmp->ice_conn = ice_conn;
|
|
xsmp->current_save_yourself = -1;
|
|
xsmp->next_save_yourself = -1;
|
|
|
|
fd = IceConnectionNumber (ice_conn);
|
|
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
|
|
|
|
channel = g_io_channel_unix_new (fd);
|
|
xsmp->watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
|
|
client_iochannel_watch, xsmp);
|
|
g_io_channel_unref (channel);
|
|
|
|
xsmp->protocol_timeout = g_timeout_add_seconds (5, client_protocol_timeout, xsmp);
|
|
|
|
set_description (xsmp);
|
|
g_debug ("New client '%s'", xsmp->description);
|
|
|
|
return xsmp;
|
|
}
|
|
|
|
static void
|
|
xsmp_finalize (GObject *object)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) object;
|
|
|
|
g_debug ("xsmp_finalize (%s)", xsmp->description);
|
|
|
|
if (xsmp->watch_id)
|
|
g_source_remove (xsmp->watch_id);
|
|
|
|
if (xsmp->conn)
|
|
SmsCleanUp (xsmp->conn);
|
|
else
|
|
IceCloseConnection (xsmp->ice_conn);
|
|
|
|
if (xsmp->protocol_timeout)
|
|
g_source_remove (xsmp->protocol_timeout);
|
|
|
|
G_OBJECT_CLASS (gsm_client_xsmp_parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
client_iochannel_watch (GIOChannel *channel,
|
|
GIOCondition condition,
|
|
gpointer data)
|
|
{
|
|
GsmClient *client = data;
|
|
GsmClientXSMP *xsmp = data;
|
|
|
|
switch (IceProcessMessages (xsmp->ice_conn, NULL, NULL))
|
|
{
|
|
case IceProcessMessagesSuccess:
|
|
return TRUE;
|
|
|
|
case IceProcessMessagesIOError:
|
|
g_debug ("IceProcessMessagesIOError on '%s'", xsmp->description);
|
|
gsm_client_disconnected (client);
|
|
return FALSE;
|
|
|
|
case IceProcessMessagesConnectionClosed:
|
|
g_debug ("IceProcessMessagesConnectionClosed on '%s'",
|
|
xsmp->description);
|
|
return FALSE;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
/* Called if too much time passes between the initial connection and
|
|
* the XSMP protocol setup.
|
|
*/
|
|
static gboolean
|
|
client_protocol_timeout (gpointer data)
|
|
{
|
|
GsmClient *client = data;
|
|
GsmClientXSMP *xsmp = data;
|
|
|
|
g_debug ("client_protocol_timeout for client '%s' in ICE status %d",
|
|
xsmp->description, IceConnectionStatus (xsmp->ice_conn));
|
|
gsm_client_disconnected (client);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static Status
|
|
register_client_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
char *previous_id)
|
|
{
|
|
GsmClient *client = manager_data;
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
char *id;
|
|
|
|
g_debug ("Client '%s' received RegisterClient(%s)",
|
|
xsmp->description,
|
|
previous_id ? previous_id : "NULL");
|
|
|
|
id = gsm_session_register_client (global_session, client, previous_id);
|
|
|
|
if (id == NULL)
|
|
{
|
|
g_debug (" rejected: invalid previous_id");
|
|
free (previous_id);
|
|
return FALSE;
|
|
}
|
|
|
|
xsmp->id = id;
|
|
|
|
set_description (xsmp);
|
|
|
|
g_debug ("Sending RegisterClientReply to '%s'", xsmp->description);
|
|
|
|
SmsRegisterClientReply (conn, xsmp->id);
|
|
|
|
if (!previous_id)
|
|
{
|
|
/* Send the initial SaveYourself. */
|
|
g_debug ("Sending initial SaveYourself");
|
|
SmsSaveYourself (conn, SmSaveLocal, False, SmInteractStyleNone, False);
|
|
xsmp->current_save_yourself = SmSaveLocal;
|
|
|
|
free (previous_id);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
do_save_yourself (GsmClientXSMP *xsmp, int save_type)
|
|
{
|
|
if (xsmp->next_save_yourself != -1)
|
|
{
|
|
/* Either we're currently doing a shutdown and there's a checkpoint
|
|
* queued after it, or vice versa. Either way, the new SaveYourself
|
|
* is redundant.
|
|
*/
|
|
g_debug (" skipping redundant SaveYourself for '%s'",
|
|
xsmp->description);
|
|
}
|
|
else if (xsmp->current_save_yourself != -1)
|
|
{
|
|
g_debug (" queuing new SaveYourself for '%s'",
|
|
xsmp->description);
|
|
xsmp->next_save_yourself = save_type;
|
|
}
|
|
else
|
|
{
|
|
xsmp->current_save_yourself = save_type;
|
|
|
|
switch (save_type)
|
|
{
|
|
case SmSaveLocal:
|
|
/* Save state */
|
|
SmsSaveYourself (xsmp->conn, SmSaveLocal, FALSE,
|
|
SmInteractStyleNone, FALSE);
|
|
break;
|
|
|
|
default:
|
|
/* Logout */
|
|
SmsSaveYourself (xsmp->conn, save_type, TRUE,
|
|
SmInteractStyleAny, FALSE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
save_yourself_request_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
int save_type,
|
|
Bool shutdown,
|
|
int interact_style,
|
|
Bool fast,
|
|
Bool global)
|
|
{
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
|
|
g_debug ("Client '%s' received SaveYourselfRequest(%s, %s, %s, %s, %s)",
|
|
xsmp->description,
|
|
save_type == SmSaveLocal ? "SmSaveLocal" :
|
|
save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
|
|
shutdown ? "Shutdown" : "!Shutdown",
|
|
interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
|
|
interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
|
|
"SmInteractStyleNone", fast ? "Fast" : "!Fast",
|
|
global ? "Global" : "!Global");
|
|
|
|
/* Examining the g_debug above, you can see that there are a total
|
|
* of 72 different combinations of options that this could have been
|
|
* called with. However, most of them are stupid.
|
|
*
|
|
* If @shutdown and @global are both TRUE, that means the caller is
|
|
* requesting that a logout message be sent to all clients, so we do
|
|
* that. We use @fast to decide whether or not to show a
|
|
* confirmation dialog. (This isn't really what @fast is for, but
|
|
* the old gnome-session and ksmserver both interpret it that way,
|
|
* so we do too.) We ignore @save_type because we pick the correct
|
|
* save_type ourselves later based on user prefs, dialog choices,
|
|
* etc, and we ignore @interact_style, because clients have not used
|
|
* it correctly consistently enough to make it worth honoring.
|
|
*
|
|
* If @shutdown is TRUE and @global is FALSE, the caller is
|
|
* confused, so we ignore the request.
|
|
*
|
|
* If @shutdown is FALSE and @save_type is SmSaveGlobal or
|
|
* SmSaveBoth, then the client wants us to ask some or all open
|
|
* applications to save open files to disk, but NOT quit. This is
|
|
* silly and so we ignore the request.
|
|
*
|
|
* If @shutdown is FALSE and @save_type is SmSaveLocal, then the
|
|
* client wants us to ask some or all open applications to update
|
|
* their current saved state, but not log out. At the moment, the
|
|
* code only supports this for the !global case (ie, a client
|
|
* requesting that it be allowed to update *its own* saved state,
|
|
* but not having everyone else update their saved state).
|
|
*/
|
|
|
|
if (shutdown && global)
|
|
{
|
|
g_debug (" initiating shutdown");
|
|
/* gsm_session_initiate_shutdown (global_session,
|
|
!fast,
|
|
GSM_SESSION_LOGOUT_TYPE_LOGOUT);
|
|
*/
|
|
}
|
|
else if (!shutdown && !global)
|
|
{
|
|
g_debug (" initiating checkpoint");
|
|
do_save_yourself (xsmp, SmSaveLocal);
|
|
}
|
|
else
|
|
g_debug (" ignoring");
|
|
}
|
|
|
|
static void
|
|
xsmp_restart (GsmClient *client, GError **error)
|
|
{
|
|
char *restart_cmd = gsm_client_get_restart_command (client);
|
|
|
|
g_spawn_command_line_async (restart_cmd, error);
|
|
|
|
g_free (restart_cmd);
|
|
}
|
|
|
|
static void
|
|
xsmp_save_yourself (GsmClient *client, gboolean save_state)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *)client;
|
|
|
|
g_debug ("xsmp_save_yourself ('%s', %s)", xsmp->description,
|
|
save_state ? "True" : "False");
|
|
|
|
do_save_yourself (xsmp, save_state ? SmSaveBoth : SmSaveGlobal);
|
|
}
|
|
|
|
static void
|
|
save_yourself_phase2_request_callback (SmsConn conn,
|
|
SmPointer manager_data)
|
|
{
|
|
GsmClient *client = manager_data;
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
|
|
g_debug ("Client '%s' received SaveYourselfPhase2Request",
|
|
xsmp->description);
|
|
|
|
if (xsmp->current_save_yourself == SmSaveLocal)
|
|
{
|
|
/* WTF? Anyway, if it's checkpointing, it doesn't have to wait
|
|
* for anyone else.
|
|
*/
|
|
SmsSaveYourselfPhase2 (xsmp->conn);
|
|
}
|
|
else
|
|
gsm_client_request_phase2 (client);
|
|
}
|
|
|
|
static void
|
|
xsmp_save_yourself_phase2 (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *)client;
|
|
|
|
g_debug ("xsmp_save_yourself_phase2 ('%s')", xsmp->description);
|
|
|
|
SmsSaveYourselfPhase2 (xsmp->conn);
|
|
}
|
|
|
|
static void
|
|
interact_request_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
int dialog_type)
|
|
{
|
|
GsmClient *client = manager_data;
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
|
|
g_debug ("Client '%s' received InteractRequest(%s)", xsmp->description,
|
|
dialog_type == SmInteractStyleAny ? "Any" : "Errors");
|
|
|
|
gsm_client_request_interaction (client);
|
|
}
|
|
|
|
static void
|
|
xsmp_interact (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
|
|
g_debug ("xsmp_interact ('%s')", xsmp->description);
|
|
|
|
SmsInteract (xsmp->conn);
|
|
}
|
|
|
|
static void
|
|
interact_done_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
Bool cancel_shutdown)
|
|
{
|
|
GsmClient *client = manager_data;
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
|
|
g_debug ("Client '%s' received InteractDone(cancel_shutdown = %s)",
|
|
xsmp->description, cancel_shutdown ? "True" : "False");
|
|
|
|
gsm_client_interaction_done (client, cancel_shutdown);
|
|
}
|
|
|
|
static void
|
|
xsmp_shutdown_cancelled (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
|
|
g_debug ("xsmp_shutdown_cancelled ('%s')", xsmp->description);
|
|
|
|
SmsShutdownCancelled (xsmp->conn);
|
|
}
|
|
|
|
static void
|
|
xsmp_die (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
|
|
g_debug ("xsmp_die ('%s')", xsmp->description);
|
|
|
|
SmsDie (xsmp->conn);
|
|
}
|
|
|
|
static void
|
|
save_yourself_done_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
Bool success)
|
|
{
|
|
GsmClient *client = manager_data;
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
|
|
g_debug ("Client '%s' received SaveYourselfDone(success = %s)",
|
|
xsmp->description, success ? "True" : "False");
|
|
|
|
if (xsmp->current_save_yourself == SmSaveLocal)
|
|
{
|
|
xsmp->current_save_yourself = -1;
|
|
SmsSaveComplete (xsmp->conn);
|
|
gsm_client_saved_state (client);
|
|
}
|
|
else
|
|
{
|
|
xsmp->current_save_yourself = -1;
|
|
gsm_client_save_yourself_done (client);
|
|
}
|
|
|
|
if (xsmp->next_save_yourself)
|
|
{
|
|
int save_type = xsmp->next_save_yourself;
|
|
|
|
xsmp->next_save_yourself = -1;
|
|
do_save_yourself (xsmp, save_type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
close_connection_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
int count,
|
|
char **reason_msgs)
|
|
{
|
|
GsmClient *client = manager_data;
|
|
GsmClientXSMP *xsmp = manager_data;
|
|
int i;
|
|
|
|
g_debug ("Client '%s' received CloseConnection", xsmp->description);
|
|
for (i = 0; i < count; i++)
|
|
g_debug (" close reason: '%s'", reason_msgs[i]);
|
|
SmFreeReasons (count, reason_msgs);
|
|
|
|
gsm_client_disconnected (client);
|
|
}
|
|
|
|
static void
|
|
debug_print_property (SmProp *prop)
|
|
{
|
|
GString *tmp;
|
|
int i;
|
|
|
|
switch (prop->type[0])
|
|
{
|
|
case 'C': /* CARD8 */
|
|
g_debug (" %s = %d", prop->name, *(unsigned char *)prop->vals[0].value);
|
|
break;
|
|
|
|
case 'A': /* ARRAY8 */
|
|
g_debug (" %s = '%s'", prop->name, (char *)prop->vals[0].value);
|
|
break;
|
|
|
|
case 'L': /* LISTofARRAY8 */
|
|
tmp = g_string_new (NULL);
|
|
for (i = 0; i < prop->num_vals; i++)
|
|
{
|
|
g_string_append_printf (tmp, "'%.*s' ", prop->vals[i].length,
|
|
(char *)prop->vals[i].value);
|
|
}
|
|
g_debug (" %s = %s", prop->name, tmp->str);
|
|
g_string_free (tmp, TRUE);
|
|
break;
|
|
|
|
default:
|
|
g_debug (" %s = ??? (%s)", prop->name, prop->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static SmProp *
|
|
find_property (GsmClientXSMP *client, const char *name, int *index)
|
|
{
|
|
SmProp *prop;
|
|
int i;
|
|
|
|
for (i = 0; i < client->props->len; i++)
|
|
{
|
|
prop = client->props->pdata[i];
|
|
|
|
if (!strcmp (prop->name, name))
|
|
{
|
|
if (index)
|
|
*index = i;
|
|
return prop;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
delete_property (GsmClientXSMP *client, const char *name)
|
|
{
|
|
int index;
|
|
SmProp *prop;
|
|
|
|
prop = find_property (client, name, &index);
|
|
if (!prop)
|
|
return;
|
|
|
|
#if 0
|
|
/* This is wrong anyway; we can't unconditionally run the current
|
|
* discard command; if this client corresponds to a GsmAppResumed,
|
|
* and the current discard command is identical to the app's
|
|
* discard_command, then we don't run the discard command now,
|
|
* because that would delete a saved state we may want to resume
|
|
* again later.
|
|
*/
|
|
if (!strcmp (name, SmDiscardCommand))
|
|
gsm_client_run_discard (client);
|
|
#endif
|
|
|
|
g_ptr_array_remove_index_fast (client->props, index);
|
|
SmFreeProperty (prop);
|
|
}
|
|
|
|
static void
|
|
set_properties_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
int num_props,
|
|
SmProp **props)
|
|
{
|
|
GsmClientXSMP *client = manager_data;
|
|
int i;
|
|
|
|
g_debug ("Set properties from client '%s'", client->description);
|
|
|
|
for (i = 0; i < num_props; i++)
|
|
{
|
|
delete_property (client, props[i]->name);
|
|
g_ptr_array_add (client->props, props[i]);
|
|
|
|
debug_print_property (props[i]);
|
|
|
|
if (!strcmp (props[i]->name, SmProgram))
|
|
set_description (client);
|
|
}
|
|
|
|
free (props);
|
|
|
|
}
|
|
|
|
static void
|
|
delete_properties_callback (SmsConn conn,
|
|
SmPointer manager_data,
|
|
int num_props,
|
|
char **prop_names)
|
|
{
|
|
GsmClientXSMP *client = manager_data;
|
|
int i;
|
|
|
|
g_debug ("Delete properties from '%s'", client->description);
|
|
|
|
for (i = 0; i < num_props; i++)
|
|
{
|
|
delete_property (client, prop_names[i]);
|
|
|
|
g_debug (" %s", prop_names[i]);
|
|
}
|
|
|
|
free (prop_names);
|
|
}
|
|
|
|
static void
|
|
get_properties_callback (SmsConn conn,
|
|
SmPointer manager_data)
|
|
{
|
|
GsmClientXSMP *client = manager_data;
|
|
|
|
g_debug ("Get properties request from '%s'", client->description);
|
|
|
|
SmsReturnProperties (conn, client->props->len,
|
|
(SmProp **)client->props->pdata);
|
|
}
|
|
|
|
static const char *
|
|
xsmp_get_client_id (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
|
|
return xsmp->id;
|
|
}
|
|
|
|
static pid_t
|
|
xsmp_get_pid (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
SmProp *prop = find_property (xsmp, SmProcessID, NULL);
|
|
char buf[32];
|
|
|
|
if (!prop || strcmp (prop->type, SmARRAY8) != 0)
|
|
return (pid_t)-1;
|
|
|
|
/* prop->vals[0].value might not be '\0'-terminated... */
|
|
g_strlcpy (buf, prop->vals[0].value, MIN (prop->vals[0].length, sizeof (buf)));
|
|
return (pid_t)strtoul (buf, NULL, 10);
|
|
}
|
|
|
|
static char *
|
|
xsmp_get_desktop_file (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
SmProp *prop = find_property (xsmp, GsmDesktopFile, NULL);
|
|
|
|
if (!prop || strcmp (prop->type, SmARRAY8) != 0)
|
|
return NULL;
|
|
|
|
return g_strndup (prop->vals[0].value, prop->vals[0].length);
|
|
}
|
|
|
|
static char *
|
|
prop_to_command (SmProp *prop)
|
|
{
|
|
GString *str;
|
|
int i, j;
|
|
gboolean need_quotes;
|
|
|
|
str = g_string_new (NULL);
|
|
for (i = 0; i < prop->num_vals; i++)
|
|
{
|
|
char *val = prop->vals[i].value;
|
|
|
|
need_quotes = FALSE;
|
|
for (j = 0; j < prop->vals[i].length; j++)
|
|
{
|
|
if (!g_ascii_isalnum (val[j]) && !strchr ("-_=:./", val[j]))
|
|
{
|
|
need_quotes = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i > 0)
|
|
g_string_append_c (str, ' ');
|
|
|
|
if (!need_quotes)
|
|
{
|
|
g_string_append_printf (str, "%.*s", prop->vals[i].length,
|
|
(char *)prop->vals[i].value);
|
|
}
|
|
else
|
|
{
|
|
g_string_append_c (str, '\'');
|
|
while (val < (char *)prop->vals[i].value + prop->vals[i].length)
|
|
{
|
|
if (*val == '\'')
|
|
g_string_append (str, "'\''");
|
|
else
|
|
g_string_append_c (str, *val);
|
|
val++;
|
|
}
|
|
g_string_append_c (str, '\'');
|
|
}
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
static char *
|
|
xsmp_get_restart_command (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
SmProp *prop = find_property (xsmp, SmRestartCommand, NULL);
|
|
|
|
if (!prop || strcmp (prop->type, SmLISTofARRAY8) != 0)
|
|
return NULL;
|
|
|
|
return prop_to_command (prop);
|
|
}
|
|
|
|
static char *
|
|
xsmp_get_discard_command (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
SmProp *prop = find_property (xsmp, SmDiscardCommand, NULL);
|
|
|
|
if (!prop || strcmp (prop->type, SmLISTofARRAY8) != 0)
|
|
return NULL;
|
|
|
|
return prop_to_command (prop);
|
|
}
|
|
|
|
static gboolean
|
|
xsmp_get_autorestart (GsmClient *client)
|
|
{
|
|
GsmClientXSMP *xsmp = (GsmClientXSMP *) client;
|
|
SmProp *prop = find_property (xsmp, SmRestartStyleHint, NULL);
|
|
|
|
if (!prop || strcmp (prop->type, SmCARD8) != 0)
|
|
return FALSE;
|
|
|
|
return ((unsigned char *)prop->vals[0].value)[0] == SmRestartImmediately;
|
|
}
|
|
|
|
static void
|
|
set_description (GsmClientXSMP *client)
|
|
{
|
|
SmProp *prop = find_property (client, SmProgram, NULL);
|
|
|
|
g_free (client->description);
|
|
if (prop)
|
|
{
|
|
client->description = g_strdup_printf ("%p [%.*s %s]", client,
|
|
prop->vals[0].length,
|
|
(char *)prop->vals[0].value,
|
|
client->id);
|
|
}
|
|
else if (client->id)
|
|
client->description = g_strdup_printf ("%p [%s]", client, client->id);
|
|
else
|
|
client->description = g_strdup_printf ("%p", client);
|
|
}
|
|
|
|
void
|
|
gsm_client_xsmp_connect (GsmClientXSMP *client, SmsConn conn,
|
|
unsigned long *mask_ret, SmsCallbacks *callbacks_ret)
|
|
{
|
|
client->conn = conn;
|
|
|
|
if (client->protocol_timeout)
|
|
{
|
|
g_source_remove (client->protocol_timeout);
|
|
client->protocol_timeout = 0;
|
|
}
|
|
|
|
g_debug ("Initializing client %s", client->description);
|
|
|
|
*mask_ret = 0;
|
|
|
|
*mask_ret |= SmsRegisterClientProcMask;
|
|
callbacks_ret->register_client.callback = register_client_callback;
|
|
callbacks_ret->register_client.manager_data = client;
|
|
|
|
*mask_ret |= SmsInteractRequestProcMask;
|
|
callbacks_ret->interact_request.callback = interact_request_callback;
|
|
callbacks_ret->interact_request.manager_data = client;
|
|
|
|
*mask_ret |= SmsInteractDoneProcMask;
|
|
callbacks_ret->interact_done.callback = interact_done_callback;
|
|
callbacks_ret->interact_done.manager_data = client;
|
|
|
|
*mask_ret |= SmsSaveYourselfRequestProcMask;
|
|
callbacks_ret->save_yourself_request.callback = save_yourself_request_callback;
|
|
callbacks_ret->save_yourself_request.manager_data = client;
|
|
|
|
*mask_ret |= SmsSaveYourselfP2RequestProcMask;
|
|
callbacks_ret->save_yourself_phase2_request.callback = save_yourself_phase2_request_callback;
|
|
callbacks_ret->save_yourself_phase2_request.manager_data = client;
|
|
|
|
*mask_ret |= SmsSaveYourselfDoneProcMask;
|
|
callbacks_ret->save_yourself_done.callback = save_yourself_done_callback;
|
|
callbacks_ret->save_yourself_done.manager_data = client;
|
|
|
|
*mask_ret |= SmsCloseConnectionProcMask;
|
|
callbacks_ret->close_connection.callback = close_connection_callback;
|
|
callbacks_ret->close_connection.manager_data = client;
|
|
|
|
*mask_ret |= SmsSetPropertiesProcMask;
|
|
callbacks_ret->set_properties.callback = set_properties_callback;
|
|
callbacks_ret->set_properties.manager_data = client;
|
|
|
|
*mask_ret |= SmsDeletePropertiesProcMask;
|
|
callbacks_ret->delete_properties.callback = delete_properties_callback;
|
|
callbacks_ret->delete_properties.manager_data = client;
|
|
|
|
*mask_ret |= SmsGetPropertiesProcMask;
|
|
callbacks_ret->get_properties.callback = get_properties_callback;
|
|
callbacks_ret->get_properties.manager_data = client;
|
|
}
|