First go at session management. Not asking review since
it's a bunch of ugly C code imported from gnome-session. Will clean it up a bit but I don't plan to make it too shiny :)
This commit is contained in:
parent
5ba50d0e0f
commit
50f79e6323
@ -5,6 +5,7 @@ sugar_PYTHON = \
|
||||
env.py \
|
||||
network.py \
|
||||
profile.py \
|
||||
session.py \
|
||||
util.py \
|
||||
wm.py
|
||||
|
||||
@ -17,13 +18,27 @@ _sugarext_la_CFLAGS = \
|
||||
$(PYTHON_INCLUDES)
|
||||
|
||||
_sugarext_la_LDFLAGS = -module -avoid-version
|
||||
_sugarext_la_LIBADD = $(EXT_LIBS)
|
||||
_sugarext_la_LIBADD = $(EXT_LIBS) -lSM -lICE
|
||||
|
||||
_sugarext_la_SOURCES = \
|
||||
$(BUILT_SOURCES) \
|
||||
_sugarextmodule.c \
|
||||
app.h \
|
||||
app.c \
|
||||
client.h \
|
||||
client.c \
|
||||
client-xsmp.h \
|
||||
client-xsmp.c \
|
||||
eggaccelerators.c \
|
||||
eggaccelerators.h \
|
||||
eggdesktopfile.h \
|
||||
eggdesktopfile.c \
|
||||
eggsmclient.h \
|
||||
eggsmclient.c \
|
||||
eggsmclient-private.h \
|
||||
eggsmclient-xsmp.c \
|
||||
session.h \
|
||||
session.c \
|
||||
sexy-icon-entry.h \
|
||||
sexy-icon-entry.c \
|
||||
sugar-address-entry.c \
|
||||
@ -33,7 +48,9 @@ _sugarext_la_SOURCES = \
|
||||
sugar-menu.h \
|
||||
sugar-menu.c \
|
||||
sugar-preview.h \
|
||||
sugar-preview.c
|
||||
sugar-preview.c \
|
||||
xsmp.h \
|
||||
xsmp.c
|
||||
|
||||
BUILT_SOURCES = \
|
||||
_sugarext.c \
|
||||
@ -45,6 +62,7 @@ _sugarext.c: _sugarext.defs _sugarext.override
|
||||
.defs.c:
|
||||
(cd $(srcdir)\
|
||||
&& $(PYGTK_CODEGEN) \
|
||||
--register $(PYGTK_DEFSDIR)/gdk-types.defs \
|
||||
--register $(PYGTK_DEFSDIR)/gdk-types.defs \
|
||||
--register $(PYGTK_DEFSDIR)/gtk-types.defs \
|
||||
--override $*.override \
|
||||
|
@ -36,6 +36,27 @@
|
||||
(gtype-id "SEXY_TYPE_ICON_ENTRY")
|
||||
)
|
||||
|
||||
(define-object SMClientXSMP
|
||||
(in-module "Egg")
|
||||
(parent "EggSMClient")
|
||||
(c-name "EggSMClientXSMP")
|
||||
(gtype-id "EGG_TYPE_SM_CLIENT_XSMP")
|
||||
)
|
||||
|
||||
(define-object SMClient
|
||||
(in-module "Egg")
|
||||
(parent "GObject")
|
||||
(c-name "EggSMClient")
|
||||
(gtype-id "EGG_TYPE_SM_CLIENT")
|
||||
)
|
||||
|
||||
(define-object Session
|
||||
(in-module "Gsm")
|
||||
(parent "GObject")
|
||||
(c-name "GsmSession")
|
||||
(gtype-id "GSM_TYPE_SESSION")
|
||||
)
|
||||
|
||||
;; Enumerations and flags ...
|
||||
|
||||
(define-enum IconEntryPosition
|
||||
@ -194,3 +215,123 @@
|
||||
(c-name "sugar_preview_get_pixbuf")
|
||||
(return-type "GdkPixbuf*")
|
||||
)
|
||||
|
||||
;; From eggsmclient.h
|
||||
|
||||
(define-function egg_sm_client_get_type
|
||||
(c-name "egg_sm_client_get_type")
|
||||
(return-type "GType")
|
||||
)
|
||||
|
||||
(define-function egg_sm_client_get_option_group
|
||||
(c-name "egg_sm_client_get_option_group")
|
||||
(return-type "GOptionGroup*")
|
||||
)
|
||||
|
||||
(define-method is_resumed
|
||||
(of-object "EggSMClient")
|
||||
(c-name "egg_sm_client_is_resumed")
|
||||
(return-type "gboolean")
|
||||
)
|
||||
|
||||
(define-method get_state_file
|
||||
(of-object "EggSMClient")
|
||||
(c-name "egg_sm_client_get_state_file")
|
||||
(return-type "GKeyFile*")
|
||||
)
|
||||
|
||||
(define-method set_restart_command
|
||||
(of-object "EggSMClient")
|
||||
(c-name "egg_sm_client_set_restart_command")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("int" "argc")
|
||||
'("const-char**" "argv")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method startup
|
||||
(of-object "EggSMClient")
|
||||
(c-name "egg_sm_client_startup")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
(define-method will_quit
|
||||
(of-object "EggSMClient")
|
||||
(c-name "egg_sm_client_will_quit")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("gboolean" "will_quit")
|
||||
)
|
||||
)
|
||||
|
||||
(define-function egg_sm_client_end_session
|
||||
(c-name "egg_sm_client_end_session")
|
||||
(return-type "gboolean")
|
||||
(parameters
|
||||
'("EggSMClientEndStyle" "style")
|
||||
'("gboolean" "request_confirmation")
|
||||
)
|
||||
)
|
||||
|
||||
;; From xsmp.h
|
||||
|
||||
(define-function xsmp_init
|
||||
(c-name "gsm_xsmp_init")
|
||||
(return-type "char*")
|
||||
)
|
||||
|
||||
(define-function xsmp_run
|
||||
(c-name "gsm_xsmp_run")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
(define-function xsmp_shutdown
|
||||
(c-name "gsm_xsmp_shutdown")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
;; From session.h
|
||||
|
||||
(define-method set_name
|
||||
(of-object "GsmSession")
|
||||
(c-name "gsm_session_set_name")
|
||||
(return-type "none")
|
||||
(parameters
|
||||
'("const-char*" "name")
|
||||
)
|
||||
)
|
||||
|
||||
(define-method start
|
||||
(of-object "GsmSession")
|
||||
(c-name "gsm_session_start")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
(define-method get_phase
|
||||
(of-object "GsmSession")
|
||||
(c-name "gsm_session_get_phase")
|
||||
(return-type "GsmSessionPhase")
|
||||
)
|
||||
|
||||
(define-method initiate_shutdown
|
||||
(of-object "GsmSession")
|
||||
(c-name "gsm_session_initiate_shutdown")
|
||||
(return-type "none")
|
||||
)
|
||||
|
||||
(define-method register_client
|
||||
(of-object "GsmSession")
|
||||
(c-name "gsm_session_register_client")
|
||||
(return-type "char*")
|
||||
(parameters
|
||||
'("GsmClient*" "client")
|
||||
'("const-char*" "previous_id")
|
||||
)
|
||||
)
|
||||
|
||||
(define-function session_create_global
|
||||
(c-name "gsm_session_create_global")
|
||||
(return-type "GsmSession*")
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,11 @@ headers
|
||||
#include "sugar-menu.h"
|
||||
#include "sugar-preview.h"
|
||||
#include "sexy-icon-entry.h"
|
||||
#include "session.h"
|
||||
|
||||
#define EGG_SM_CLIENT_BACKEND_XSMP
|
||||
#include "eggsmclient.h"
|
||||
#include "eggsmclient-private.h"
|
||||
|
||||
#include <pygtk/pygtk.h>
|
||||
#include <glib.h>
|
||||
|
@ -69,6 +69,7 @@ from sugar.graphics.toolcombobox import ToolComboBox
|
||||
from sugar.graphics.alert import Alert
|
||||
from sugar.graphics.icon import Icon
|
||||
from sugar.datastore import datastore
|
||||
from sugar.session import XSMPClient
|
||||
from sugar import wm
|
||||
from sugar import profile
|
||||
from sugar import _sugarext
|
||||
@ -435,6 +436,11 @@ class Activity(Window, gtk.Container):
|
||||
self._max_participants = 0
|
||||
self._invites_queue = []
|
||||
|
||||
self._xsmp_client = XSMPClient()
|
||||
self._xsmp_client.connect('quit-requested', self.__sm_quit_requested_cb)
|
||||
self._xsmp_client.connect('quit', self.__sm_quit_cb)
|
||||
self._xsmp_client.startup()
|
||||
|
||||
accel_group = gtk.AccelGroup()
|
||||
self.set_data('sugar-accel-group', accel_group)
|
||||
self.add_accel_group(accel_group)
|
||||
@ -556,6 +562,13 @@ class Activity(Window, gtk.Container):
|
||||
Window.set_canvas(self, canvas)
|
||||
canvas.connect('map', self.__canvas_map_cb)
|
||||
|
||||
def __sm_quit_requested_cb(self, client):
|
||||
client.will_quit(True)
|
||||
|
||||
def __sm_quit_cb(self, client):
|
||||
print 'sm quit'
|
||||
self.close(force=True)
|
||||
|
||||
def __canvas_map_cb(self, canvas):
|
||||
if self._jobject and self._jobject.file_path:
|
||||
self.read_file(self._jobject.file_path)
|
||||
|
396
src/sugar/app.c
Normal file
396
src/sugar/app.c
Normal file
@ -0,0 +1,396 @@
|
||||
/* app.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 <glib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "app.h"
|
||||
|
||||
enum {
|
||||
EXITED,
|
||||
REGISTERED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_DESKTOP_FILE,
|
||||
PROP_CLIENT_ID,
|
||||
|
||||
LAST_PROP
|
||||
};
|
||||
|
||||
static void set_property (GObject *object, guint prop_id,
|
||||
const GValue *value, GParamSpec *pspec);
|
||||
static void get_property (GObject *object, guint prop_id,
|
||||
GValue *value, GParamSpec *pspec);
|
||||
static void dispose (GObject *object);
|
||||
|
||||
static const char *get_basename (GsmApp *app);
|
||||
static pid_t launch (GsmApp *app, GError **err);
|
||||
|
||||
G_DEFINE_TYPE (GsmApp, gsm_app, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gsm_app_init (GsmApp *app)
|
||||
{
|
||||
app->pid = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
gsm_app_class_init (GsmAppClass *app_class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (app_class);
|
||||
|
||||
object_class->set_property = set_property;
|
||||
object_class->get_property = get_property;
|
||||
object_class->dispose = dispose;
|
||||
|
||||
app_class->get_basename = get_basename;
|
||||
app_class->launch = launch;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_DESKTOP_FILE,
|
||||
g_param_spec_string ("desktop-file",
|
||||
"Desktop file",
|
||||
"Freedesktop .desktop file",
|
||||
NULL,
|
||||
G_PARAM_READWRITE));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_CLIENT_ID,
|
||||
g_param_spec_string ("client-id",
|
||||
"Client ID",
|
||||
"Session management client ID",
|
||||
NULL,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
signals[EXITED] =
|
||||
g_signal_new ("exited",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmAppClass, exited),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
signals[REGISTERED] =
|
||||
g_signal_new ("registered",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmAppClass, registered),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
||||
static void
|
||||
set_property (GObject *object, guint prop_id,
|
||||
const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
GsmApp *app = GSM_APP (object);
|
||||
const char *desktop_file;
|
||||
char *phase;
|
||||
GError *error = NULL;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DESKTOP_FILE:
|
||||
if (app->desktop_file)
|
||||
egg_desktop_file_free (app->desktop_file);
|
||||
desktop_file = g_value_get_string (value);
|
||||
if (!desktop_file)
|
||||
{
|
||||
app->desktop_file = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
app->desktop_file = egg_desktop_file_new (desktop_file, &error);
|
||||
if (!app->desktop_file)
|
||||
{
|
||||
g_warning ("Could not parse desktop file %s: %s",
|
||||
desktop_file, error->message);
|
||||
g_error_free (error);
|
||||
break;
|
||||
}
|
||||
|
||||
phase = egg_desktop_file_get_string (app->desktop_file,
|
||||
"X-GNOME-Autostart-Phase", NULL);
|
||||
if (phase)
|
||||
{
|
||||
if (!strcmp (phase, "Initialization"))
|
||||
app->phase = GSM_SESSION_PHASE_INITIALIZATION;
|
||||
else if (!strcmp (phase, "WindowManager"))
|
||||
app->phase = GSM_SESSION_PHASE_WINDOW_MANAGER;
|
||||
else if (!strcmp (phase, "Panel"))
|
||||
app->phase = GSM_SESSION_PHASE_PANEL;
|
||||
else if (!strcmp (phase, "Desktop"))
|
||||
app->phase = GSM_SESSION_PHASE_DESKTOP;
|
||||
else
|
||||
app->phase = GSM_SESSION_PHASE_APPLICATION;
|
||||
|
||||
g_free (phase);
|
||||
}
|
||||
else
|
||||
app->phase = GSM_SESSION_PHASE_APPLICATION;
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_ID:
|
||||
g_free (app->client_id);
|
||||
app->client_id = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_property (GObject *object, guint prop_id,
|
||||
GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
GsmApp *app = GSM_APP (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DESKTOP_FILE:
|
||||
if (app->desktop_file)
|
||||
g_value_set_string (value, egg_desktop_file_get_source (app->desktop_file));
|
||||
else
|
||||
g_value_set_string (value, NULL);
|
||||
break;
|
||||
|
||||
case PROP_CLIENT_ID:
|
||||
g_value_set_string (value, app->client_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dispose(GObject *object)
|
||||
{
|
||||
GsmApp *app = GSM_APP (object);
|
||||
|
||||
if (app->desktop_file)
|
||||
{
|
||||
egg_desktop_file_free (app->desktop_file);
|
||||
app->desktop_file = NULL;
|
||||
}
|
||||
|
||||
if (app->startup_id)
|
||||
{
|
||||
g_free (app->startup_id);
|
||||
app->startup_id = NULL;
|
||||
}
|
||||
|
||||
if (app->client_id)
|
||||
{
|
||||
g_free (app->client_id);
|
||||
app->client_id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_app_get_basename:
|
||||
* @app: a %GsmApp
|
||||
*
|
||||
* Returns an identifying name for @app, e.g. the basename of the path to
|
||||
* @app's desktop file (if any).
|
||||
*
|
||||
* Return value: an identifying name for @app, or %NULL.
|
||||
**/
|
||||
const char *
|
||||
gsm_app_get_basename (GsmApp *app)
|
||||
{
|
||||
return GSM_APP_GET_CLASS (app)->get_basename (app);
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_basename (GsmApp *app)
|
||||
{
|
||||
const char *location, *slash;
|
||||
|
||||
if (!app->desktop_file)
|
||||
return NULL;
|
||||
|
||||
location = egg_desktop_file_get_source (app->desktop_file);
|
||||
|
||||
slash = strrchr (location, '/');
|
||||
if (slash)
|
||||
return slash + 1;
|
||||
else
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_app_get_phase:
|
||||
* @app: a %GsmApp
|
||||
*
|
||||
* Returns @app's startup phase.
|
||||
*
|
||||
* Return value: @app's startup phase
|
||||
**/
|
||||
GsmSessionPhase
|
||||
gsm_app_get_phase (GsmApp *app)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_APP (app), GSM_SESSION_PHASE_APPLICATION);
|
||||
|
||||
return app->phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_app_is_disabled:
|
||||
* @app: a %GsmApp
|
||||
*
|
||||
* Tests if @app is disabled
|
||||
*
|
||||
* Return value: whether or not @app is disabled
|
||||
**/
|
||||
gboolean
|
||||
gsm_app_is_disabled (GsmApp *app)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
|
||||
|
||||
if (GSM_APP_GET_CLASS (app)->is_disabled)
|
||||
return GSM_APP_GET_CLASS (app)->is_disabled (app);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsm_app_provides (GsmApp *app, const char *service)
|
||||
{
|
||||
char **provides;
|
||||
gsize len, i;
|
||||
|
||||
g_return_val_if_fail (GSM_IS_APP (app), FALSE);
|
||||
|
||||
if (!app->desktop_file)
|
||||
return FALSE;
|
||||
|
||||
provides = egg_desktop_file_get_string_list (app->desktop_file,
|
||||
"X-GNOME-Provides",
|
||||
&len, NULL);
|
||||
if (!provides)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (!strcmp (provides[i], service))
|
||||
{
|
||||
g_strfreev (provides);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (provides);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
app_exited (GPid pid, gint status, gpointer data)
|
||||
{
|
||||
if (WIFEXITED (status))
|
||||
g_signal_emit (GSM_APP (data), signals[EXITED], 0);
|
||||
}
|
||||
|
||||
static pid_t
|
||||
launch (GsmApp *app,
|
||||
GError **err)
|
||||
{
|
||||
char *env[2] = { NULL, NULL };
|
||||
gboolean success;
|
||||
|
||||
g_return_val_if_fail (app->desktop_file != NULL, (pid_t)-1);
|
||||
|
||||
if (egg_desktop_file_get_boolean (app->desktop_file,
|
||||
"X-GNOME-Autostart-Notify", NULL) ||
|
||||
egg_desktop_file_get_boolean (app->desktop_file,
|
||||
"AutostartNotify", NULL))
|
||||
env[0] = g_strdup_printf ("DESKTOP_AUTOSTART_ID=%s", app->client_id);
|
||||
|
||||
#if 0
|
||||
g_debug ("launching %s with client_id %s\n",
|
||||
gsm_app_get_basename (app), app->client_id);
|
||||
#endif
|
||||
|
||||
success =
|
||||
egg_desktop_file_launch (app->desktop_file, NULL, err,
|
||||
EGG_DESKTOP_FILE_LAUNCH_PUTENV, env,
|
||||
EGG_DESKTOP_FILE_LAUNCH_FLAGS, G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, &app->pid,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID, &app->startup_id,
|
||||
NULL);
|
||||
|
||||
g_free (env[0]);
|
||||
|
||||
if (success)
|
||||
{
|
||||
/* In case the app belongs to Initialization phase, we monitor
|
||||
* if it exits to emit proper "exited" signal to session. */
|
||||
if (app->phase == GSM_SESSION_PHASE_INITIALIZATION)
|
||||
g_child_watch_add ((GPid) app->pid, app_exited, app);
|
||||
|
||||
return app->pid;
|
||||
}
|
||||
else
|
||||
return (pid_t) -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_app_launch:
|
||||
* @app: a %GsmApp
|
||||
* @err: an error pointer
|
||||
*
|
||||
* Launches @app
|
||||
*
|
||||
* Return value: the pid of the new process, or -1 on error
|
||||
**/
|
||||
pid_t
|
||||
gsm_app_launch (GsmApp *app, GError **err)
|
||||
{
|
||||
return GSM_APP_GET_CLASS (app)->launch (app, err);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_app_registered:
|
||||
* @app: a %GsmApp
|
||||
*
|
||||
* Emits "registered" signal in @app
|
||||
**/
|
||||
void
|
||||
gsm_app_registered (GsmApp *app)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_APP (app));
|
||||
|
||||
g_signal_emit (app, signals[REGISTERED], 0);
|
||||
}
|
||||
|
70
src/sugar/app.h
Normal file
70
src/sugar/app.h
Normal file
@ -0,0 +1,70 @@
|
||||
/* gsmapp.h
|
||||
* Copyright (C) 2006 Novell, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GSM_APP_H__
|
||||
#define __GSM_APP_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "eggdesktopfile.h"
|
||||
#include "session.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSM_TYPE_APP (gsm_app_get_type ())
|
||||
#define GSM_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_APP, GsmApp))
|
||||
#define GSM_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_APP, GsmAppClass))
|
||||
#define GSM_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_APP))
|
||||
#define GSM_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_APP))
|
||||
#define GSM_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_APP, GsmAppClass))
|
||||
|
||||
typedef struct _GsmApp GsmApp;
|
||||
typedef struct _GsmAppClass GsmAppClass;
|
||||
typedef struct _GsmAppPrivate GsmAppPrivate;
|
||||
|
||||
struct _GsmApp
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
EggDesktopFile *desktop_file;
|
||||
GsmSessionPhase phase;
|
||||
|
||||
pid_t pid;
|
||||
char *startup_id, *client_id;
|
||||
};
|
||||
|
||||
struct _GsmAppClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*exited) (GsmApp *app, int status);
|
||||
void (*registered) (GsmApp *app);
|
||||
|
||||
/* virtual methods */
|
||||
const char *(*get_basename) (GsmApp *app);
|
||||
gboolean (*is_disabled) (GsmApp *app);
|
||||
pid_t (*launch) (GsmApp *app, GError **err);
|
||||
void (*set_client) (GsmApp *app, GsmClient *client);
|
||||
};
|
||||
|
||||
GType gsm_app_get_type (void) G_GNUC_CONST;
|
||||
|
||||
const char *gsm_app_get_basename (GsmApp *app);
|
||||
GsmSessionPhase gsm_app_get_phase (GsmApp *app);
|
||||
gboolean gsm_app_provides (GsmApp *app,
|
||||
const char *service);
|
||||
gboolean gsm_app_is_disabled (GsmApp *app);
|
||||
pid_t gsm_app_launch (GsmApp *app,
|
||||
GError **err);
|
||||
void gsm_app_set_client (GsmApp *app,
|
||||
GsmClient *client);
|
||||
|
||||
void gsm_app_registered (GsmApp *app);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSM_APP_H__ */
|
828
src/sugar/client-xsmp.c
Normal file
828
src/sugar/client-xsmp.c
Normal file
@ -0,0 +1,828 @@
|
||||
/* 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 "client-xsmp.h"
|
||||
#include "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 (5000, 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;
|
||||
}
|
70
src/sugar/client-xsmp.h
Normal file
70
src/sugar/client-xsmp.h
Normal file
@ -0,0 +1,70 @@
|
||||
/* client-xsmp.h
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GSM_CLIENT_XSMP_H__
|
||||
#define __GSM_CLIENT_XSMP_H__
|
||||
|
||||
#include "client.h"
|
||||
|
||||
#include <X11/SM/SMlib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSM_TYPE_CLIENT_XSMP (gsm_client_xsmp_get_type ())
|
||||
#define GSM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_CLIENT_XSMP, GsmClientXSMP))
|
||||
#define GSM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_CLIENT_XSMP, GsmClientXSMPClass))
|
||||
#define GSM_IS_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_CLIENT_XSMP))
|
||||
#define GSM_IS_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_CLIENT_XSMP))
|
||||
#define GSM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_CLIENT_XSMP, GsmClientXSMPClass))
|
||||
|
||||
typedef struct _GsmClientXSMP GsmClientXSMP;
|
||||
typedef struct _GsmClientXSMPClass GsmClientXSMPClass;
|
||||
|
||||
struct _GsmClientXSMP
|
||||
{
|
||||
GsmClient parent;
|
||||
|
||||
SmsConn conn;
|
||||
IceConn ice_conn;
|
||||
|
||||
guint watch_id, protocol_timeout;
|
||||
|
||||
int current_save_yourself, next_save_yourself;
|
||||
char *id, *description;
|
||||
GPtrArray *props;
|
||||
};
|
||||
|
||||
struct _GsmClientXSMPClass
|
||||
{
|
||||
GsmClientClass parent_class;
|
||||
|
||||
};
|
||||
|
||||
GType gsm_client_xsmp_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GsmClientXSMP *gsm_client_xsmp_new (IceConn ice_conn);
|
||||
|
||||
void gsm_client_xsmp_connect (GsmClientXSMP *client,
|
||||
SmsConn conn,
|
||||
unsigned long *mask_ret,
|
||||
SmsCallbacks *callbacks_ret);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSM_CLIENT_XSMP_H__ */
|
251
src/sugar/client.c
Normal file
251
src/sugar/client.c
Normal file
@ -0,0 +1,251 @@
|
||||
/* client.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 "client.h"
|
||||
|
||||
enum {
|
||||
SAVED_STATE,
|
||||
REQUEST_PHASE2,
|
||||
REQUEST_INTERACTION,
|
||||
INTERACTION_DONE,
|
||||
SAVE_YOURSELF_DONE,
|
||||
DISCONNECTED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (GsmClient, gsm_client, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gsm_client_init (GsmClient *client)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
gsm_client_class_init (GsmClientClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
signals[SAVED_STATE] =
|
||||
g_signal_new ("saved_state",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmClientClass, saved_state),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
signals[REQUEST_PHASE2] =
|
||||
g_signal_new ("request_phase2",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmClientClass, request_phase2),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
signals[REQUEST_INTERACTION] =
|
||||
g_signal_new ("request_interaction",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmClientClass, request_interaction),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
signals[INTERACTION_DONE] =
|
||||
g_signal_new ("interaction_done",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmClientClass, interaction_done),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOOLEAN,
|
||||
G_TYPE_NONE,
|
||||
1, G_TYPE_BOOLEAN);
|
||||
|
||||
signals[SAVE_YOURSELF_DONE] =
|
||||
g_signal_new ("save_yourself_done",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmClientClass, save_yourself_done),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
signals[DISCONNECTED] =
|
||||
g_signal_new ("disconnected",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmClientClass, disconnected),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
}
|
||||
|
||||
const char *
|
||||
gsm_client_get_client_id (GsmClient *client)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
|
||||
|
||||
return GSM_CLIENT_GET_CLASS (client)->get_client_id (client);
|
||||
}
|
||||
|
||||
pid_t
|
||||
gsm_client_get_pid (GsmClient *client)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_CLIENT (client), -1);
|
||||
|
||||
return GSM_CLIENT_GET_CLASS (client)->get_pid (client);
|
||||
}
|
||||
|
||||
char *
|
||||
gsm_client_get_desktop_file (GsmClient *client)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
|
||||
|
||||
return GSM_CLIENT_GET_CLASS (client)->get_desktop_file (client);
|
||||
}
|
||||
|
||||
char *
|
||||
gsm_client_get_restart_command (GsmClient *client)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
|
||||
|
||||
return GSM_CLIENT_GET_CLASS (client)->get_restart_command (client);
|
||||
}
|
||||
|
||||
char *
|
||||
gsm_client_get_discard_command (GsmClient *client)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
|
||||
|
||||
return GSM_CLIENT_GET_CLASS (client)->get_discard_command (client);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsm_client_get_autorestart (GsmClient *client)
|
||||
{
|
||||
g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
|
||||
|
||||
return GSM_CLIENT_GET_CLASS (client)->get_autorestart (client);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_save_state (GsmClient *client)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_restart (GsmClient *client, GError **error)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
|
||||
GSM_CLIENT_GET_CLASS (client)->restart (client, error);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_save_yourself (GsmClient *client,
|
||||
gboolean save_state)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
|
||||
GSM_CLIENT_GET_CLASS (client)->save_yourself (client, save_state);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_save_yourself_phase2 (GsmClient *client)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
|
||||
GSM_CLIENT_GET_CLASS (client)->save_yourself_phase2 (client);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_interact (GsmClient *client)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
|
||||
GSM_CLIENT_GET_CLASS (client)->interact (client);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_shutdown_cancelled (GsmClient *client)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
|
||||
GSM_CLIENT_GET_CLASS (client)->shutdown_cancelled (client);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_die (GsmClient *client)
|
||||
{
|
||||
g_return_if_fail (GSM_IS_CLIENT (client));
|
||||
|
||||
GSM_CLIENT_GET_CLASS (client)->die (client);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_saved_state (GsmClient *client)
|
||||
{
|
||||
g_signal_emit (client, signals[SAVED_STATE], 0);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_request_phase2 (GsmClient *client)
|
||||
{
|
||||
g_signal_emit (client, signals[REQUEST_PHASE2], 0);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_request_interaction (GsmClient *client)
|
||||
{
|
||||
g_signal_emit (client, signals[REQUEST_INTERACTION], 0);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_interaction_done (GsmClient *client, gboolean cancel_shutdown)
|
||||
{
|
||||
g_signal_emit (client, signals[INTERACTION_DONE], 0, cancel_shutdown);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_save_yourself_done (GsmClient *client)
|
||||
{
|
||||
g_signal_emit (client, signals[SAVE_YOURSELF_DONE], 0);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_client_disconnected (GsmClient *client)
|
||||
{
|
||||
g_signal_emit (client, signals[DISCONNECTED], 0);
|
||||
}
|
||||
|
111
src/sugar/client.h
Normal file
111
src/sugar/client.h
Normal file
@ -0,0 +1,111 @@
|
||||
/* client.h
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GSM_CLIENT_H__
|
||||
#define __GSM_CLIENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSM_TYPE_CLIENT (gsm_client_get_type ())
|
||||
#define GSM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_CLIENT, GsmClient))
|
||||
#define GSM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_CLIENT, GsmClientClass))
|
||||
#define GSM_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_CLIENT))
|
||||
#define GSM_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_CLIENT))
|
||||
#define GSM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_CLIENT, GsmClientClass))
|
||||
|
||||
typedef struct _GsmClient GsmClient;
|
||||
typedef struct _GsmClientClass GsmClientClass;
|
||||
|
||||
struct _GsmClient
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
};
|
||||
|
||||
struct _GsmClientClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*saved_state) (GsmClient *client);
|
||||
|
||||
void (*request_phase2) (GsmClient *client);
|
||||
|
||||
void (*request_interaction) (GsmClient *client);
|
||||
void (*interaction_done) (GsmClient *client,
|
||||
gboolean cancel_shutdown);
|
||||
|
||||
void (*save_yourself_done) (GsmClient *client);
|
||||
|
||||
void (*disconnected) (GsmClient *client);
|
||||
|
||||
/* virtual methods */
|
||||
const char * (*get_client_id) (GsmClient *client);
|
||||
pid_t (*get_pid) (GsmClient *client);
|
||||
char * (*get_desktop_file) (GsmClient *client);
|
||||
char * (*get_restart_command) (GsmClient *client);
|
||||
char * (*get_discard_command) (GsmClient *client);
|
||||
gboolean (*get_autorestart) (GsmClient *client);
|
||||
|
||||
void (*restart) (GsmClient *client,
|
||||
GError **error);
|
||||
void (*save_yourself) (GsmClient *client,
|
||||
gboolean save_state);
|
||||
void (*save_yourself_phase2) (GsmClient *client);
|
||||
void (*interact) (GsmClient *client);
|
||||
void (*shutdown_cancelled) (GsmClient *client);
|
||||
void (*die) (GsmClient *client);
|
||||
};
|
||||
|
||||
GType gsm_client_get_type (void) G_GNUC_CONST;
|
||||
|
||||
const char *gsm_client_get_client_id (GsmClient *client);
|
||||
|
||||
pid_t gsm_client_get_pid (GsmClient *client);
|
||||
char *gsm_client_get_desktop_file (GsmClient *client);
|
||||
char *gsm_client_get_restart_command (GsmClient *client);
|
||||
char *gsm_client_get_discard_command (GsmClient *client);
|
||||
gboolean gsm_client_get_autorestart (GsmClient *client);
|
||||
|
||||
void gsm_client_save_state (GsmClient *client);
|
||||
|
||||
void gsm_client_restart (GsmClient *client,
|
||||
GError **error);
|
||||
void gsm_client_save_yourself (GsmClient *client,
|
||||
gboolean save_state);
|
||||
void gsm_client_save_yourself_phase2 (GsmClient *client);
|
||||
void gsm_client_interact (GsmClient *client);
|
||||
void gsm_client_shutdown_cancelled (GsmClient *client);
|
||||
void gsm_client_die (GsmClient *client);
|
||||
|
||||
/* protected */
|
||||
void gsm_client_saved_state (GsmClient *client);
|
||||
void gsm_client_request_phase2 (GsmClient *client);
|
||||
void gsm_client_request_interaction (GsmClient *client);
|
||||
void gsm_client_interaction_done (GsmClient *client,
|
||||
gboolean cancel_shutdown);
|
||||
void gsm_client_save_yourself_done (GsmClient *client);
|
||||
void gsm_client_disconnected (GsmClient *client);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSM_CLIENT_H__ */
|
1437
src/sugar/eggdesktopfile.c
Normal file
1437
src/sugar/eggdesktopfile.c
Normal file
File diff suppressed because it is too large
Load Diff
156
src/sugar/eggdesktopfile.h
Normal file
156
src/sugar/eggdesktopfile.h
Normal file
@ -0,0 +1,156 @@
|
||||
/* eggdesktopfile.h - Freedesktop.Org Desktop Files
|
||||
* Copyright (C) 2007 Novell, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public
|
||||
* License along with this library; see the file COPYING.LIB. If not,
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place -
|
||||
* Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __EGG_DESKTOP_FILE_H__
|
||||
#define __EGG_DESKTOP_FILE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct EggDesktopFile EggDesktopFile;
|
||||
|
||||
typedef enum {
|
||||
EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED,
|
||||
|
||||
EGG_DESKTOP_FILE_TYPE_APPLICATION,
|
||||
EGG_DESKTOP_FILE_TYPE_LINK,
|
||||
EGG_DESKTOP_FILE_TYPE_DIRECTORY,
|
||||
} EggDesktopFileType;
|
||||
|
||||
EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path,
|
||||
GError **error);
|
||||
|
||||
EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
|
||||
GError **error);
|
||||
EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *key_file,
|
||||
const char *source,
|
||||
GError **error);
|
||||
|
||||
void egg_desktop_file_free (EggDesktopFile *desktop_file);
|
||||
|
||||
const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file) G_GNUC_PURE;
|
||||
|
||||
EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file) G_GNUC_PURE;
|
||||
|
||||
const char *egg_desktop_file_get_name (EggDesktopFile *desktop_file) G_GNUC_PURE;
|
||||
const char *egg_desktop_file_get_icon (EggDesktopFile *desktop_file) G_GNUC_PURE;
|
||||
|
||||
gboolean egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
|
||||
const char *desktop_environment);
|
||||
|
||||
gboolean egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file);
|
||||
gboolean egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file);
|
||||
gboolean egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file);
|
||||
|
||||
char *egg_desktop_file_parse_exec (EggDesktopFile *desktop_file,
|
||||
GSList *documents,
|
||||
GError **error);
|
||||
|
||||
gboolean egg_desktop_file_launch (EggDesktopFile *desktop_file,
|
||||
GSList *documents,
|
||||
GError **error,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
|
||||
typedef enum {
|
||||
EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1,
|
||||
EGG_DESKTOP_FILE_LAUNCH_PUTENV,
|
||||
EGG_DESKTOP_FILE_LAUNCH_SCREEN,
|
||||
EGG_DESKTOP_FILE_LAUNCH_WORKSPACE,
|
||||
EGG_DESKTOP_FILE_LAUNCH_DIRECTORY,
|
||||
EGG_DESKTOP_FILE_LAUNCH_TIME,
|
||||
EGG_DESKTOP_FILE_LAUNCH_FLAGS,
|
||||
EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_PID,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE,
|
||||
EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID
|
||||
} EggDesktopFileLaunchOption;
|
||||
|
||||
/* Standard Keys */
|
||||
#define EGG_DESKTOP_FILE_GROUP "Desktop Entry"
|
||||
|
||||
#define EGG_DESKTOP_FILE_KEY_TYPE "Type"
|
||||
#define EGG_DESKTOP_FILE_KEY_VERSION "Version"
|
||||
#define EGG_DESKTOP_FILE_KEY_NAME "Name"
|
||||
#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME "GenericName"
|
||||
#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY "NoDisplay"
|
||||
#define EGG_DESKTOP_FILE_KEY_COMMENT "Comment"
|
||||
#define EGG_DESKTOP_FILE_KEY_ICON "Icon"
|
||||
#define EGG_DESKTOP_FILE_KEY_HIDDEN "Hidden"
|
||||
#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN "OnlyShowIn"
|
||||
#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN "NotShowIn"
|
||||
#define EGG_DESKTOP_FILE_KEY_TRY_EXEC "TryExec"
|
||||
#define EGG_DESKTOP_FILE_KEY_EXEC "Exec"
|
||||
#define EGG_DESKTOP_FILE_KEY_PATH "Path"
|
||||
#define EGG_DESKTOP_FILE_KEY_TERMINAL "Terminal"
|
||||
#define EGG_DESKTOP_FILE_KEY_MIME_TYPE "MimeType"
|
||||
#define EGG_DESKTOP_FILE_KEY_CATEGORIES "Categories"
|
||||
#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY "StartupNotify"
|
||||
#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass"
|
||||
#define EGG_DESKTOP_FILE_KEY_URL "URL"
|
||||
|
||||
/* Accessors */
|
||||
gboolean egg_desktop_file_has_key (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
GError **error);
|
||||
char *egg_desktop_file_get_string (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
GError **error) G_GNUC_MALLOC;
|
||||
char *egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
const char *locale,
|
||||
GError **error) G_GNUC_MALLOC;
|
||||
gboolean egg_desktop_file_get_boolean (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
GError **error);
|
||||
double egg_desktop_file_get_numeric (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
GError **error);
|
||||
char **egg_desktop_file_get_string_list (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
gsize *length,
|
||||
GError **error) G_GNUC_MALLOC;
|
||||
char **egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file,
|
||||
const char *key,
|
||||
const char *locale,
|
||||
gsize *length,
|
||||
GError **error) G_GNUC_MALLOC;
|
||||
|
||||
|
||||
/* Errors */
|
||||
#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark()
|
||||
|
||||
GQuark egg_desktop_file_error_quark (void);
|
||||
|
||||
typedef enum {
|
||||
EGG_DESKTOP_FILE_ERROR_INVALID,
|
||||
EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
|
||||
EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
|
||||
} EggDesktopFileError;
|
||||
|
||||
/* Global application desktop file */
|
||||
void egg_set_desktop_file (const char *desktop_file_path);
|
||||
EggDesktopFile *egg_get_desktop_file (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __EGG_DESKTOP_FILE_H__ */
|
54
src/sugar/eggsmclient-private.h
Normal file
54
src/sugar/eggsmclient-private.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* eggsmclient-private.h
|
||||
* Copyright (C) 2007 Novell, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __EGG_SM_CLIENT_PRIVATE_H__
|
||||
#define __EGG_SM_CLIENT_PRIVATE_H__
|
||||
|
||||
#include <gdkconfig.h>
|
||||
#include "eggsmclient.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GKeyFile *egg_sm_client_save_state (EggSMClient *client);
|
||||
void egg_sm_client_quit_requested (EggSMClient *client);
|
||||
void egg_sm_client_quit_cancelled (EggSMClient *client);
|
||||
void egg_sm_client_quit (EggSMClient *client);
|
||||
|
||||
#if defined (GDK_WINDOWING_X11)
|
||||
# ifdef EGG_SM_CLIENT_BACKEND_XSMP
|
||||
#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
|
||||
GType egg_sm_client_xsmp_get_type (void);
|
||||
EggSMClient *egg_sm_client_xsmp_new (void);
|
||||
# endif
|
||||
# ifdef EGG_SM_CLIENT_BACKEND_DBUS
|
||||
GType egg_sm_client_dbus_get_type (void);
|
||||
EggSMClient *egg_sm_client_dbus_new (void);
|
||||
# endif
|
||||
#elif defined (GDK_WINDOWING_WIN32)
|
||||
GType egg_sm_client_win32_get_type (void);
|
||||
EggSMClient *egg_sm_client_win32_new (void);
|
||||
#elif defined (GDK_WINDOWING_QUARTZ)
|
||||
GType egg_sm_client_osx_get_type (void);
|
||||
EggSMClient *egg_sm_client_osx_new (void);
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
|
1359
src/sugar/eggsmclient-xsmp.c
Normal file
1359
src/sugar/eggsmclient-xsmp.c
Normal file
File diff suppressed because it is too large
Load Diff
392
src/sugar/eggsmclient.c
Normal file
392
src/sugar/eggsmclient.c
Normal file
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Novell, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; 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 <string.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "eggsmclient.h"
|
||||
#include "eggsmclient-private.h"
|
||||
|
||||
static void egg_sm_client_debug_handler (const char *log_domain,
|
||||
GLogLevelFlags log_level,
|
||||
const char *message,
|
||||
gpointer user_data);
|
||||
|
||||
enum {
|
||||
SAVE_STATE,
|
||||
QUIT_REQUESTED,
|
||||
QUIT_CANCELLED,
|
||||
QUIT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
struct _EggSMClientPrivate {
|
||||
GKeyFile *state_file;
|
||||
};
|
||||
|
||||
#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate))
|
||||
|
||||
G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
egg_sm_client_init (EggSMClient *client)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
egg_sm_client_class_init (EggSMClientClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (EggSMClientPrivate));
|
||||
|
||||
/**
|
||||
* EggSMClient::save_state:
|
||||
* @client: the client
|
||||
* @state_file: a #GKeyFile to save state information into
|
||||
*
|
||||
* Emitted when the session manager has requested that the
|
||||
* application save information about its current state. The
|
||||
* application should save its state into @state_file, and then the
|
||||
* session manager may then restart the application in a future
|
||||
* session and tell it to initialize itself from that state.
|
||||
*
|
||||
* You should not save any data into @state_file's "start group"
|
||||
* (ie, the %NULL group). Instead, applications should save their
|
||||
* data into groups with names that start with the application name,
|
||||
* and libraries that connect to this signal should save their data
|
||||
* into groups with names that start with the library name.
|
||||
*
|
||||
* Alternatively, rather than (or in addition to) using @state_file,
|
||||
* the application can save its state by calling
|
||||
* egg_sm_client_set_restart_command() during the processing of this
|
||||
* signal (eg, to include a list of files to open).
|
||||
**/
|
||||
signals[SAVE_STATE] =
|
||||
g_signal_new ("save_state",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, save_state),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__POINTER,
|
||||
G_TYPE_NONE,
|
||||
1, G_TYPE_POINTER);
|
||||
|
||||
/**
|
||||
* EggSMClient::quit_requested:
|
||||
* @client: the client
|
||||
*
|
||||
* Emitted when the session manager requests that the application
|
||||
* exit (generally because the user is logging out). The application
|
||||
* should decide whether or not it is willing to quit (perhaps after
|
||||
* asking the user what to do with documents that have unsaved
|
||||
* changes) and then call egg_sm_client_will_quit(), passing %TRUE
|
||||
* or %FALSE to give its answer to the session manager. (It does not
|
||||
* need to give an answer before returning from the signal handler;
|
||||
* it can interact with the user asynchronously and then give its
|
||||
* answer later on.) If the application does not connect to this
|
||||
* signal, then #EggSMClient will automatically return %TRUE on its
|
||||
* behalf.
|
||||
*
|
||||
* The application should not save its session state as part of
|
||||
* handling this signal; if the user has requested that the session
|
||||
* be saved when logging out, then ::save_state will be emitted
|
||||
* separately.
|
||||
*
|
||||
* If the application agrees to quit, it should then wait for either
|
||||
* the ::quit_cancelled or ::quit signals to be emitted.
|
||||
**/
|
||||
signals[QUIT_REQUESTED] =
|
||||
g_signal_new ("quit_requested",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
/**
|
||||
* EggSMClient::quit_cancelled:
|
||||
* @client: the client
|
||||
*
|
||||
* Emitted when the session manager decides to cancel a logout after
|
||||
* the application has already agreed to quit. After receiving this
|
||||
* signal, the application can go back to what it was doing before
|
||||
* receiving the ::quit_requested signal.
|
||||
**/
|
||||
signals[QUIT_CANCELLED] =
|
||||
g_signal_new ("quit_cancelled",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
/**
|
||||
* EggSMClient::quit:
|
||||
* @client: the client
|
||||
*
|
||||
* Emitted when the session manager wants the application to quit
|
||||
* (generally because the user is logging out). The application
|
||||
* should exit as soon as possible after receiving this signal; if
|
||||
* it does not, the session manager may choose to forcibly kill it.
|
||||
*
|
||||
* Normally a GUI application would only be sent a ::quit if it
|
||||
* agreed to quit in response to a ::quit_requested signal. However,
|
||||
* this is not guaranteed; in some situations the session manager
|
||||
* may decide to end the session without giving applications a
|
||||
* chance to object.
|
||||
**/
|
||||
signals[QUIT] =
|
||||
g_signal_new ("quit",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (EggSMClientClass, quit),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
||||
static gboolean sm_client_disable = FALSE;
|
||||
static char *sm_client_state_file = NULL;
|
||||
static char *sm_client_id = NULL;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{ "sm-client-disable", 0, 0,
|
||||
G_OPTION_ARG_NONE, &sm_client_disable,
|
||||
N_("Disable connection to session manager"), NULL },
|
||||
{ "sm-client-state-file", 0, 0,
|
||||
G_OPTION_ARG_STRING, &sm_client_state_file,
|
||||
N_("Specify file containing saved configuration"), N_("FILE") },
|
||||
{ "sm-client-id", 0, 0,
|
||||
G_OPTION_ARG_STRING, &sm_client_id,
|
||||
N_("Specify session management ID"), N_("ID") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/**
|
||||
* egg_sm_client_is_resumed:
|
||||
* @client: the client
|
||||
*
|
||||
* Checks whether or not the current session has been resumed from
|
||||
* a previous saved session. If so, the application should call
|
||||
* egg_sm_client_get_state_file() and restore its state from the
|
||||
* returned #GKeyFile.
|
||||
*
|
||||
* Return value: %TRUE if the session has been resumed
|
||||
**/
|
||||
gboolean
|
||||
egg_sm_client_is_resumed (EggSMClient *client)
|
||||
{
|
||||
return sm_client_state_file != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_get_state_file:
|
||||
* @client: the client
|
||||
*
|
||||
* If the application was resumed by the session manager, this will
|
||||
* return the #GKeyFile containing its state from the previous
|
||||
* session.
|
||||
*
|
||||
* Note that other libraries and #EggSMClient itself may also store
|
||||
* state in the key file, so if you call egg_sm_client_get_groups(),
|
||||
* on it, the return value will likely include groups that you did not
|
||||
* put there yourself. (It is also not guaranteed that the first
|
||||
* group created by the application will still be the "start group"
|
||||
* when it is resumed.)
|
||||
*
|
||||
* Return value: the #GKeyFile containing the application's earlier
|
||||
* state, or %NULL on error. You should not free this key file; it
|
||||
* is owned by @client.
|
||||
**/
|
||||
GKeyFile *
|
||||
egg_sm_client_get_state_file (EggSMClient *client)
|
||||
{
|
||||
EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client);
|
||||
char *state_file_path;
|
||||
GError *err = NULL;
|
||||
|
||||
if (!sm_client_state_file)
|
||||
return NULL;
|
||||
if (priv->state_file)
|
||||
return priv->state_file;
|
||||
|
||||
if (!strncmp (sm_client_state_file, "file://", 7))
|
||||
state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL);
|
||||
else
|
||||
state_file_path = g_strdup (sm_client_state_file);
|
||||
|
||||
priv->state_file = g_key_file_new ();
|
||||
if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err))
|
||||
{
|
||||
g_warning ("Could not load SM state file '%s': %s",
|
||||
sm_client_state_file, err->message);
|
||||
g_clear_error (&err);
|
||||
g_key_file_free (priv->state_file);
|
||||
priv->state_file = NULL;
|
||||
}
|
||||
|
||||
g_free (state_file_path);
|
||||
return priv->state_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_set_restart_command:
|
||||
* @client: the client
|
||||
* @argc: the length of @argv
|
||||
* @argv: argument vector
|
||||
*
|
||||
* Sets the command used to restart @client if it does not have a
|
||||
* .desktop file that can be used to find its restart command.
|
||||
*
|
||||
* This can also be used when handling the ::save_state signal, to
|
||||
* save the current state via an updated command line. (Eg, providing
|
||||
* a list of filenames to open when the application is resumed.)
|
||||
**/
|
||||
void
|
||||
egg_sm_client_set_restart_command (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv)
|
||||
{
|
||||
g_return_if_fail (EGG_IS_SM_CLIENT (client));
|
||||
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_sm_client_will_quit:
|
||||
* @client: the client
|
||||
* @will_quit: whether or not the application is willing to quit
|
||||
*
|
||||
* This MUST be called in response to the ::quit_requested signal, to
|
||||
* indicate whether or not the application is willing to quit. The
|
||||
* application may call it either directly from the signal handler, or
|
||||
* at some later point (eg, after asynchronously interacting with the
|
||||
* user).
|
||||
*
|
||||
* If the application does not connect to ::quit_requested,
|
||||
* #EggSMClient will call this method on its behalf (passing %TRUE
|
||||
* for @will_quit).
|
||||
*
|
||||
* After calling this method, the application should wait to receive
|
||||
* either ::quit_cancelled or ::quit.
|
||||
**/
|
||||
void
|
||||
egg_sm_client_will_quit (EggSMClient *client,
|
||||
gboolean will_quit)
|
||||
{
|
||||
g_return_if_fail (EGG_IS_SM_CLIENT (client));
|
||||
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit);
|
||||
}
|
||||
|
||||
/* Signal-emitting callbacks from platform-specific code */
|
||||
|
||||
GKeyFile *
|
||||
egg_sm_client_save_state (EggSMClient *client)
|
||||
{
|
||||
GKeyFile *state_file;
|
||||
char *group;
|
||||
|
||||
state_file = g_key_file_new ();
|
||||
|
||||
g_debug ("Emitting save_state");
|
||||
g_signal_emit (client, signals[SAVE_STATE], 0, state_file);
|
||||
g_debug ("Done emitting save_state");
|
||||
|
||||
group = g_key_file_get_start_group (state_file);
|
||||
if (group)
|
||||
{
|
||||
g_free (group);
|
||||
return state_file;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_key_file_free (state_file);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
egg_sm_client_quit_requested (EggSMClient *client)
|
||||
{
|
||||
if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE))
|
||||
{
|
||||
g_debug ("Not emitting quit_requested because no one is listening");
|
||||
egg_sm_client_will_quit (client, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Emitting quit_requested");
|
||||
g_signal_emit (client, signals[QUIT_REQUESTED], 0);
|
||||
g_debug ("Done emitting quit_requested");
|
||||
}
|
||||
|
||||
void
|
||||
egg_sm_client_quit_cancelled (EggSMClient *client)
|
||||
{
|
||||
g_debug ("Emitting quit_cancelled");
|
||||
g_signal_emit (client, signals[QUIT_CANCELLED], 0);
|
||||
g_debug ("Done emitting quit_cancelled");
|
||||
}
|
||||
|
||||
void
|
||||
egg_sm_client_quit (EggSMClient *client)
|
||||
{
|
||||
g_debug ("Emitting quit");
|
||||
g_signal_emit (client, signals[QUIT], 0);
|
||||
g_debug ("Done emitting quit");
|
||||
|
||||
/* FIXME: should we just call gtk_main_quit() here? */
|
||||
}
|
||||
|
||||
void
|
||||
egg_sm_client_startup (EggSMClient *client)
|
||||
{
|
||||
if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
|
||||
EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
|
||||
}
|
||||
|
||||
static void
|
||||
egg_sm_client_debug_handler (const char *log_domain,
|
||||
GLogLevelFlags log_level,
|
||||
const char *message,
|
||||
gpointer user_data)
|
||||
{
|
||||
static int debug = -1;
|
||||
|
||||
if (debug < 0)
|
||||
debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
|
||||
|
||||
if (debug)
|
||||
g_log_default_handler (log_domain, log_level, message, NULL);
|
||||
}
|
112
src/sugar/eggsmclient.h
Normal file
112
src/sugar/eggsmclient.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* eggsmclient.h
|
||||
* Copyright (C) 2007 Novell, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __EGG_SM_CLIENT_H__
|
||||
#define __EGG_SM_CLIENT_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define EGG_TYPE_SM_CLIENT (egg_sm_client_get_type ())
|
||||
#define EGG_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient))
|
||||
#define EGG_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass))
|
||||
#define EGG_IS_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT))
|
||||
#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT))
|
||||
#define EGG_SM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass))
|
||||
|
||||
typedef struct _EggSMClient EggSMClient;
|
||||
typedef struct _EggSMClientClass EggSMClientClass;
|
||||
typedef struct _EggSMClientPrivate EggSMClientPrivate;
|
||||
|
||||
typedef enum {
|
||||
EGG_SM_CLIENT_END_SESSION_DEFAULT,
|
||||
EGG_SM_CLIENT_LOGOUT,
|
||||
EGG_SM_CLIENT_REBOOT,
|
||||
EGG_SM_CLIENT_SHUTDOWN
|
||||
} EggSMClientEndStyle;
|
||||
|
||||
typedef enum {
|
||||
EGG_SM_CLIENT_MODE_DISABLED,
|
||||
EGG_SM_CLIENT_MODE_NO_RESTART,
|
||||
EGG_SM_CLIENT_MODE_NORMAL
|
||||
} EggSMClientMode;
|
||||
|
||||
struct _EggSMClient
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
};
|
||||
|
||||
struct _EggSMClientClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*save_state) (EggSMClient *client,
|
||||
GKeyFile *state_file);
|
||||
|
||||
void (*quit_requested) (EggSMClient *client);
|
||||
void (*quit_cancelled) (EggSMClient *client);
|
||||
void (*quit) (EggSMClient *client);
|
||||
|
||||
/* virtual methods */
|
||||
void (*startup) (EggSMClient *client,
|
||||
const char *client_id);
|
||||
void (*set_restart_command) (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv);
|
||||
void (*will_quit) (EggSMClient *client,
|
||||
gboolean will_quit);
|
||||
gboolean (*end_session) (EggSMClient *client,
|
||||
EggSMClientEndStyle style,
|
||||
gboolean request_confirmation);
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_egg_reserved1) (void);
|
||||
void (*_egg_reserved2) (void);
|
||||
void (*_egg_reserved3) (void);
|
||||
void (*_egg_reserved4) (void);
|
||||
};
|
||||
|
||||
GType egg_sm_client_get_type (void) G_GNUC_CONST;
|
||||
|
||||
/* Resuming a saved session */
|
||||
gboolean egg_sm_client_is_resumed (EggSMClient *client);
|
||||
GKeyFile *egg_sm_client_get_state_file (EggSMClient *client);
|
||||
|
||||
/* Alternate means of saving state */
|
||||
void egg_sm_client_set_restart_command (EggSMClient *client,
|
||||
int argc,
|
||||
const char **argv);
|
||||
|
||||
/* Handling "quit_requested" signal */
|
||||
void egg_sm_client_will_quit (EggSMClient *client,
|
||||
gboolean will_quit);
|
||||
|
||||
void egg_sm_client_startup (EggSMClient *client);
|
||||
|
||||
/* Initiate a logout/reboot/shutdown */
|
||||
gboolean egg_sm_client_end_session (EggSMClientEndStyle style,
|
||||
gboolean request_confirmation);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __EGG_SM_CLIENT_H__ */
|
550
src/sugar/session.c
Normal file
550
src/sugar/session.c
Normal file
@ -0,0 +1,550 @@
|
||||
/* session.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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "session.h"
|
||||
#include "app.h"
|
||||
#include "xsmp.h"
|
||||
|
||||
GsmSession *global_session;
|
||||
|
||||
static void initiate_shutdown (GsmSession *session);
|
||||
|
||||
static void session_shutdown (GsmSession *session);
|
||||
|
||||
static void client_saved_state (GsmClient *client,
|
||||
gpointer data);
|
||||
static void client_request_phase2 (GsmClient *client,
|
||||
gpointer data);
|
||||
static void client_request_interaction (GsmClient *client,
|
||||
gpointer data);
|
||||
static void client_interaction_done (GsmClient *client,
|
||||
gboolean cancel_shutdown,
|
||||
gpointer data);
|
||||
static void client_save_yourself_done (GsmClient *client,
|
||||
gpointer data);
|
||||
static void client_disconnected (GsmClient *client,
|
||||
gpointer data);
|
||||
|
||||
struct _GsmSession {
|
||||
GObject parent;
|
||||
|
||||
char *name;
|
||||
|
||||
/* Current status */
|
||||
GsmSessionPhase phase;
|
||||
guint timeout;
|
||||
GSList *pending_apps;
|
||||
|
||||
/* SM clients */
|
||||
GSList *clients;
|
||||
|
||||
/* When shutdown starts, all clients are put into shutdown_clients.
|
||||
* If they request phase2, they are moved from shutdown_clients to
|
||||
* phase2_clients. If they request interaction, they are appended
|
||||
* to interact_clients (the first client in interact_clients is
|
||||
* the one currently interacting). If they report that they're done,
|
||||
* they're removed from shutdown_clients/phase2_clients.
|
||||
*
|
||||
* Once shutdown_clients is empty, phase2 starts. Once phase2_clients
|
||||
* is empty, shutdown is complete.
|
||||
*/
|
||||
GSList *shutdown_clients;
|
||||
GSList *interact_clients;
|
||||
GSList *phase2_clients;
|
||||
|
||||
/* List of clients which were disconnected due to disabled condition
|
||||
* and shouldn't be automatically restarted */
|
||||
GSList *condition_clients;
|
||||
};
|
||||
|
||||
struct _GsmSessionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* shutdown_completed) (GsmSession *client);
|
||||
};
|
||||
|
||||
enum {
|
||||
SHUTDOWN_COMPLETED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (GsmSession, gsm_session, G_TYPE_OBJECT)
|
||||
|
||||
#define GSM_SESSION_PHASE_TIMEOUT 10 /* seconds */
|
||||
|
||||
void
|
||||
gsm_session_init (GsmSession *session)
|
||||
{
|
||||
session->name = NULL;
|
||||
session->clients = NULL;
|
||||
session->condition_clients = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gsm_session_class_init (GsmSessionClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
signals[SHUTDOWN_COMPLETED] =
|
||||
g_signal_new ("shutdown_completed",
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GsmSessionClass, shutdown_completed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gsm_session_set_name:
|
||||
* @session: session instance
|
||||
* @name: name of the session
|
||||
*
|
||||
* Sets the name of a running session.
|
||||
**/
|
||||
void
|
||||
gsm_session_set_name (GsmSession *session, const char *name)
|
||||
{
|
||||
if (session->name)
|
||||
g_free (session->name);
|
||||
|
||||
session->name = g_strdup (name);
|
||||
}
|
||||
|
||||
static void start_phase (GsmSession *session);
|
||||
|
||||
static void
|
||||
end_phase (GsmSession *session)
|
||||
{
|
||||
g_slist_free (session->pending_apps);
|
||||
session->pending_apps = NULL;
|
||||
|
||||
g_debug ("ending phase %d\n", session->phase);
|
||||
|
||||
session->phase++;
|
||||
|
||||
if (session->phase < GSM_SESSION_PHASE_RUNNING)
|
||||
start_phase (session);
|
||||
}
|
||||
|
||||
static void
|
||||
app_condition_changed (GsmApp *app, gboolean condition, gpointer data)
|
||||
{
|
||||
GsmSession *session;
|
||||
GsmClient *client = NULL;
|
||||
GSList *cl = NULL;
|
||||
|
||||
g_return_if_fail (data != NULL);
|
||||
|
||||
session = (GsmSession *) data;
|
||||
|
||||
/* Check for an existing session client for this app */
|
||||
for (cl = session->clients; cl; cl = cl->next)
|
||||
{
|
||||
GsmClient *c = GSM_CLIENT (cl->data);
|
||||
|
||||
if (!strcmp (app->client_id, gsm_client_get_client_id (c)))
|
||||
client = c;
|
||||
}
|
||||
|
||||
if (condition)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (app->pid <= 0 && client == NULL)
|
||||
gsm_app_launch (app, &error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
g_warning ("Not able to launch autostart app from its condition: %s",
|
||||
error->message);
|
||||
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Kill client in case condition if false and make sure it won't
|
||||
* be automatically restarted by adding the client to
|
||||
* condition_clients */
|
||||
session->condition_clients =
|
||||
g_slist_prepend (session->condition_clients, client);
|
||||
gsm_client_die (client);
|
||||
app->pid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_registered (GsmApp *app, gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
|
||||
session->pending_apps = g_slist_remove (session->pending_apps, app);
|
||||
g_signal_handlers_disconnect_by_func (app, app_registered, session);
|
||||
|
||||
if (!session->pending_apps)
|
||||
{
|
||||
if (session->timeout > 0)
|
||||
{
|
||||
g_source_remove (session->timeout);
|
||||
session->timeout = 0;
|
||||
}
|
||||
|
||||
end_phase (session);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
phase_timeout (gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
GSList *a;
|
||||
|
||||
session->timeout = 0;
|
||||
|
||||
for (a = session->pending_apps; a; a = a->next)
|
||||
{
|
||||
g_warning ("Application '%s' failed to register before timeout",
|
||||
gsm_app_get_basename (a->data));
|
||||
g_signal_handlers_disconnect_by_func (a->data, app_registered, session);
|
||||
|
||||
/* FIXME: what if the app was filling in a required slot? */
|
||||
}
|
||||
|
||||
end_phase (session);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
start_phase (GsmSession *session)
|
||||
{
|
||||
GsmApp *app;
|
||||
GSList *a;
|
||||
GError *err = NULL;
|
||||
|
||||
g_debug ("starting phase %d\n", session->phase);
|
||||
|
||||
g_slist_free (session->pending_apps);
|
||||
session->pending_apps = NULL;
|
||||
|
||||
if (session->pending_apps)
|
||||
{
|
||||
if (session->phase < GSM_SESSION_PHASE_APPLICATION)
|
||||
{
|
||||
session->timeout = g_timeout_add (GSM_SESSION_PHASE_TIMEOUT * 1000,
|
||||
phase_timeout, session);
|
||||
}
|
||||
}
|
||||
else
|
||||
end_phase (session);
|
||||
}
|
||||
|
||||
void
|
||||
gsm_session_start (GsmSession *session)
|
||||
{
|
||||
session->phase = GSM_SESSION_PHASE_INITIALIZATION;
|
||||
|
||||
start_phase (session);
|
||||
}
|
||||
|
||||
GsmSessionPhase
|
||||
gsm_session_get_phase (GsmSession *session)
|
||||
{
|
||||
return session->phase;
|
||||
}
|
||||
|
||||
char *
|
||||
gsm_session_register_client (GsmSession *session,
|
||||
GsmClient *client,
|
||||
const char *id)
|
||||
{
|
||||
GSList *a;
|
||||
char *client_id = NULL;
|
||||
|
||||
/* If we're shutting down, we don't accept any new session
|
||||
clients. */
|
||||
if (session->phase == GSM_SESSION_PHASE_SHUTDOWN)
|
||||
return FALSE;
|
||||
|
||||
if (id == NULL)
|
||||
client_id = gsm_xsmp_generate_client_id ();
|
||||
else
|
||||
{
|
||||
for (a = session->clients; a; a = a->next)
|
||||
{
|
||||
GsmClient *client = GSM_CLIENT (a->data);
|
||||
|
||||
/* We can't have two clients with the same id. */
|
||||
if (!strcmp (id, gsm_client_get_client_id (client)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
client_id = g_strdup (id);
|
||||
}
|
||||
|
||||
g_debug ("Adding new client %s to session", id);
|
||||
|
||||
g_signal_connect (client, "saved_state",
|
||||
G_CALLBACK (client_saved_state), session);
|
||||
g_signal_connect (client, "request_phase2",
|
||||
G_CALLBACK (client_request_phase2), session);
|
||||
g_signal_connect (client, "request_interaction",
|
||||
G_CALLBACK (client_request_interaction), session);
|
||||
g_signal_connect (client, "interaction_done",
|
||||
G_CALLBACK (client_interaction_done), session);
|
||||
g_signal_connect (client, "save_yourself_done",
|
||||
G_CALLBACK (client_save_yourself_done), session);
|
||||
g_signal_connect (client, "disconnected",
|
||||
G_CALLBACK (client_disconnected), session);
|
||||
|
||||
session->clients = g_slist_prepend (session->clients, client);
|
||||
|
||||
/* If it's a brand new client id, we just accept the client*/
|
||||
if (id == NULL)
|
||||
return client_id;
|
||||
|
||||
/* If we're starting up the session, try to match the new client
|
||||
* with one pending apps for the current phase. If not, try to match
|
||||
* with any of the autostarted apps. */
|
||||
if (session->phase < GSM_SESSION_PHASE_APPLICATION)
|
||||
a = session->pending_apps;
|
||||
|
||||
for (; a; a = a->next)
|
||||
{
|
||||
GsmApp *app = GSM_APP (a->data);
|
||||
|
||||
if (!strcmp (client_id, app->client_id))
|
||||
{
|
||||
gsm_app_registered (app);
|
||||
return client_id;
|
||||
}
|
||||
}
|
||||
|
||||
g_free (client_id);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
client_saved_state (GsmClient *client, gpointer data)
|
||||
{
|
||||
/* FIXME */
|
||||
}
|
||||
|
||||
void
|
||||
gsm_session_initiate_shutdown (GsmSession *session)
|
||||
{
|
||||
gboolean logout_prompt;
|
||||
|
||||
if (session->phase == GSM_SESSION_PHASE_SHUTDOWN)
|
||||
{
|
||||
/* Already shutting down, nothing more to do */
|
||||
return;
|
||||
}
|
||||
|
||||
initiate_shutdown (session);
|
||||
}
|
||||
|
||||
static void
|
||||
session_shutdown_phase2 (GsmSession *session)
|
||||
{
|
||||
GSList *cl;
|
||||
|
||||
for (cl = session->phase2_clients; cl; cl = cl->next)
|
||||
gsm_client_save_yourself_phase2 (cl->data);
|
||||
}
|
||||
|
||||
static void
|
||||
session_cancel_shutdown (GsmSession *session)
|
||||
{
|
||||
GSList *cl;
|
||||
|
||||
session->phase = GSM_SESSION_PHASE_RUNNING;
|
||||
|
||||
g_slist_free (session->shutdown_clients);
|
||||
session->shutdown_clients = NULL;
|
||||
g_slist_free (session->interact_clients);
|
||||
session->interact_clients = NULL;
|
||||
g_slist_free (session->phase2_clients);
|
||||
session->phase2_clients = NULL;
|
||||
|
||||
for (cl = session->clients; cl; cl = cl->next)
|
||||
gsm_client_shutdown_cancelled (cl->data);
|
||||
}
|
||||
|
||||
static void
|
||||
initiate_shutdown (GsmSession *session)
|
||||
{
|
||||
GSList *cl;
|
||||
|
||||
session->phase = GSM_SESSION_PHASE_SHUTDOWN;
|
||||
|
||||
if (session->clients == NULL)
|
||||
session_shutdown (session);
|
||||
|
||||
for (cl = session->clients; cl; cl = cl->next)
|
||||
{
|
||||
GsmClient *client = GSM_CLIENT (cl->data);
|
||||
|
||||
session->shutdown_clients =
|
||||
g_slist_prepend (session->shutdown_clients, client);
|
||||
|
||||
gsm_client_save_yourself (client, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
session_shutdown (GsmSession *session)
|
||||
{
|
||||
GSList *cl;
|
||||
|
||||
/* FIXME: do this in reverse phase order */
|
||||
for (cl = session->clients; cl; cl = cl->next)
|
||||
gsm_client_die (cl->data);
|
||||
}
|
||||
|
||||
static void
|
||||
client_request_phase2 (GsmClient *client, gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
|
||||
/* Move the client from shutdown_clients to phase2_clients */
|
||||
|
||||
session->shutdown_clients =
|
||||
g_slist_remove (session->shutdown_clients, client);
|
||||
session->phase2_clients =
|
||||
g_slist_prepend (session->phase2_clients, client);
|
||||
}
|
||||
|
||||
static void
|
||||
client_request_interaction (GsmClient *client, gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
|
||||
session->interact_clients =
|
||||
g_slist_append (session->interact_clients, client);
|
||||
|
||||
if (!session->interact_clients->next)
|
||||
gsm_client_interact (client);
|
||||
}
|
||||
|
||||
static void
|
||||
client_interaction_done (GsmClient *client, gboolean cancel_shutdown,
|
||||
gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
|
||||
g_return_if_fail (session->interact_clients &&
|
||||
(GsmClient *)session->interact_clients->data == client);
|
||||
|
||||
if (cancel_shutdown)
|
||||
{
|
||||
session_cancel_shutdown (session);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove this client from interact_clients, and if there's another
|
||||
* client waiting to interact, let it interact now.
|
||||
*/
|
||||
session->interact_clients =
|
||||
g_slist_remove (session->interact_clients, client);
|
||||
if (session->interact_clients)
|
||||
gsm_client_interact (session->interact_clients->data);
|
||||
}
|
||||
|
||||
static void
|
||||
client_save_yourself_done (GsmClient *client, gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
|
||||
session->shutdown_clients =
|
||||
g_slist_remove (session->shutdown_clients, client);
|
||||
session->interact_clients =
|
||||
g_slist_remove (session->interact_clients, client);
|
||||
session->phase2_clients =
|
||||
g_slist_remove (session->phase2_clients, client);
|
||||
|
||||
if (session->phase == GSM_SESSION_PHASE_SHUTDOWN &&
|
||||
!session->shutdown_clients)
|
||||
{
|
||||
if (session->phase2_clients)
|
||||
session_shutdown_phase2 (session);
|
||||
else
|
||||
session_shutdown (session);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_disconnected (GsmClient *client, gpointer data)
|
||||
{
|
||||
GsmSession *session = data;
|
||||
gboolean is_condition_client = FALSE;
|
||||
|
||||
session->clients =
|
||||
g_slist_remove (session->clients, client);
|
||||
session->shutdown_clients =
|
||||
g_slist_remove (session->shutdown_clients, client);
|
||||
session->interact_clients =
|
||||
g_slist_remove (session->interact_clients, client);
|
||||
session->phase2_clients =
|
||||
g_slist_remove (session->phase2_clients, client);
|
||||
|
||||
if (g_slist_find (session->condition_clients, client))
|
||||
{
|
||||
session->condition_clients =
|
||||
g_slist_remove (session->condition_clients, client);
|
||||
|
||||
is_condition_client = TRUE;
|
||||
}
|
||||
|
||||
if (session->phase != GSM_SESSION_PHASE_SHUTDOWN &&
|
||||
gsm_client_get_autorestart (client) &&
|
||||
!is_condition_client)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
gsm_client_restart (client, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Error on restarting session client: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (client);
|
||||
|
||||
g_signal_emit (session, signals[SHUTDOWN_COMPLETED], 0);
|
||||
}
|
||||
|
||||
GsmSession *
|
||||
gsm_session_create_global (void)
|
||||
{
|
||||
global_session = GSM_SESSION(g_object_new (GSM_TYPE_SESSION, NULL));
|
||||
return global_session;
|
||||
}
|
93
src/sugar/session.h
Normal file
93
src/sugar/session.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* session.h
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GSM_SESSION_H__
|
||||
#define __GSM_SESSION_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "client.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSM_TYPE_SESSION (gsm_session_get_type ())
|
||||
#define GSM_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SESSION, GsmSession))
|
||||
#define GSM_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SESSION, GsmSessionClass))
|
||||
#define GSM_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SESSION))
|
||||
#define GSM_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SESSION))
|
||||
#define GSM_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_SESSION, GsmSessionClass))
|
||||
|
||||
typedef struct _GsmSession GsmSession;
|
||||
typedef struct _GsmSessionClass GsmSessionClass;
|
||||
extern GsmSession *global_session;
|
||||
|
||||
typedef enum {
|
||||
/* gsm's own startup/initialization phase */
|
||||
GSM_SESSION_PHASE_STARTUP,
|
||||
|
||||
/* xrandr setup, gnome-settings-daemon, etc */
|
||||
GSM_SESSION_PHASE_INITIALIZATION,
|
||||
|
||||
/* window/compositing managers */
|
||||
GSM_SESSION_PHASE_WINDOW_MANAGER,
|
||||
|
||||
/* apps that will create _NET_WM_WINDOW_TYPE_PANEL windows */
|
||||
GSM_SESSION_PHASE_PANEL,
|
||||
|
||||
/* apps that will create _NET_WM_WINDOW_TYPE_DESKTOP windows */
|
||||
GSM_SESSION_PHASE_DESKTOP,
|
||||
|
||||
/* everything else */
|
||||
GSM_SESSION_PHASE_APPLICATION,
|
||||
|
||||
/* done launching */
|
||||
GSM_SESSION_PHASE_RUNNING,
|
||||
|
||||
/* shutting down */
|
||||
GSM_SESSION_PHASE_SHUTDOWN
|
||||
} GsmSessionPhase;
|
||||
|
||||
typedef enum {
|
||||
GSM_SESSION_LOGOUT_TYPE_LOGOUT,
|
||||
GSM_SESSION_LOGOUT_TYPE_SHUTDOWN
|
||||
} GsmSessionLogoutType;
|
||||
|
||||
typedef enum {
|
||||
GSM_SESSION_LOGOUT_MODE_NORMAL,
|
||||
GSM_SESSION_LOGOUT_MODE_NO_CONFIRMATION,
|
||||
GSM_SESSION_LOGOUT_MODE_FORCE
|
||||
} GsmSessionLogoutMode;
|
||||
|
||||
void gsm_session_set_name (GsmSession *session,
|
||||
const char *name);
|
||||
|
||||
void gsm_session_start (GsmSession *session);
|
||||
|
||||
GsmSessionPhase gsm_session_get_phase (GsmSession *session);
|
||||
|
||||
void gsm_session_initiate_shutdown (GsmSession *session);
|
||||
|
||||
char *gsm_session_register_client (GsmSession *session,
|
||||
GsmClient *client,
|
||||
const char *previous_id);
|
||||
|
||||
GsmSession *gsm_session_create_global (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSM_SESSION_H__ */
|
46
src/sugar/session.py
Normal file
46
src/sugar/session.py
Normal file
@ -0,0 +1,46 @@
|
||||
# Copyright (C) 2008, Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library 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 Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
import os
|
||||
|
||||
from sugar import _sugarext
|
||||
|
||||
class XSMPClient(_sugarext.SMClientXSMP):
|
||||
def __init__(self):
|
||||
_sugarext.SMClientXSMP.__init__(self)
|
||||
|
||||
class Session(object):
|
||||
def __init__(self):
|
||||
address = _sugarext.xsmp_init()
|
||||
os.environ['SESSION_MANAGER'] = address
|
||||
_sugarext.xsmp_run()
|
||||
|
||||
self.session = _sugarext.session_create_global()
|
||||
|
||||
def start(self):
|
||||
self.session.start()
|
||||
self.session.connect('shutdown_completed',
|
||||
self.__shutdown_completed_cb)
|
||||
|
||||
def initiate_shutdown(self, ):
|
||||
self.session.initiate_shutdown()
|
||||
|
||||
def shutdown_completed(self):
|
||||
pass
|
||||
|
||||
def __shutdown_completed_cb(self, session):
|
||||
self.shutdown_completed()
|
537
src/sugar/xsmp.c
Normal file
537
src/sugar/xsmp.c
Normal file
@ -0,0 +1,537 @@
|
||||
/* 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 <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "client-xsmp.h"
|
||||
//#include "gsm.h"
|
||||
//#include "util.h"
|
||||
#include "xsmp.h"
|
||||
|
||||
#include <X11/ICE/ICElib.h>
|
||||
#include <X11/ICE/ICEutil.h>
|
||||
#include <X11/ICE/ICEconn.h>
|
||||
#include <X11/SM/SMlib.h>
|
||||
|
||||
#ifdef HAVE_X11_XTRANS_XTRANS_H
|
||||
/* Get the proto for _IceTransNoListen */
|
||||
#define ICE_t
|
||||
#define TRANS_SERVER
|
||||
#include <X11/Xtrans/Xtrans.h>
|
||||
#undef ICE_t
|
||||
#undef TRANS_SERVER
|
||||
#endif /* HAVE_X11_XTRANS_XTRANS_H */
|
||||
|
||||
static IceListenObj *xsmp_sockets;
|
||||
static int num_xsmp_sockets, num_local_xsmp_sockets;
|
||||
|
||||
static gboolean update_iceauthority (gboolean adding);
|
||||
|
||||
static gboolean accept_ice_connection (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data);
|
||||
static Status accept_xsmp_connection (SmsConn conn,
|
||||
SmPointer manager_data,
|
||||
unsigned long *mask_ret,
|
||||
SmsCallbacks *callbacks_ret,
|
||||
char **failure_reason_ret);
|
||||
|
||||
static void ice_error_handler (IceConn conn,
|
||||
Bool swap,
|
||||
int offending_minor_opcode,
|
||||
unsigned long offending_sequence_num,
|
||||
int error_class,
|
||||
int severity,
|
||||
IcePointer values);
|
||||
static void ice_io_error_handler (IceConn conn);
|
||||
static void sms_error_handler (SmsConn sms_conn,
|
||||
Bool swap,
|
||||
int offending_minor_opcode,
|
||||
unsigned long offending_sequence_num,
|
||||
int error_class,
|
||||
int severity,
|
||||
IcePointer values);
|
||||
/**
|
||||
* gsm_xsmp_init:
|
||||
*
|
||||
* Initializes XSMP. Notably, it creates the XSMP listening socket and
|
||||
* sets the SESSION_MANAGER environment variable to point to it.
|
||||
**/
|
||||
char *
|
||||
gsm_xsmp_init (void)
|
||||
{
|
||||
char error[256];
|
||||
mode_t saved_umask;
|
||||
char *network_id_list;
|
||||
int i;
|
||||
|
||||
/* Set up sane error handlers */
|
||||
IceSetErrorHandler (ice_error_handler);
|
||||
IceSetIOErrorHandler (ice_io_error_handler);
|
||||
SmsSetErrorHandler (sms_error_handler);
|
||||
|
||||
/* Initialize libSM; we pass NULL for hostBasedAuthProc to disable
|
||||
* host-based authentication.
|
||||
*/
|
||||
if (!SmsInitialize (PACKAGE, VERSION, accept_xsmp_connection,
|
||||
NULL, NULL, sizeof (error), error))
|
||||
g_error("Could not initialize libSM: %s", error);
|
||||
|
||||
#ifdef HAVE_X11_XTRANS_XTRANS_H
|
||||
/* By default, IceListenForConnections will open one socket for each
|
||||
* transport type known to X. We don't want connections from remote
|
||||
* hosts, so for security reasons it would be best if ICE didn't
|
||||
* even open any non-local sockets. So we use an internal ICElib
|
||||
* method to disable them here. Unfortunately, there is no way to
|
||||
* ask X what transport types it knows about, so we're forced to
|
||||
* guess.
|
||||
*/
|
||||
_IceTransNoListen ("tcp");
|
||||
#endif
|
||||
|
||||
/* Create the XSMP socket. Older versions of IceListenForConnections
|
||||
* have a bug which causes the umask to be set to 0 on certain types
|
||||
* of failures. Probably not an issue on any modern systems, but
|
||||
* we'll play it safe.
|
||||
*/
|
||||
saved_umask = umask (0);
|
||||
umask (saved_umask);
|
||||
if (!IceListenForConnections (&num_xsmp_sockets, &xsmp_sockets,
|
||||
sizeof (error), error))
|
||||
g_error ("Could not create ICE listening socket: %s", error);
|
||||
umask (saved_umask);
|
||||
|
||||
/* Find the local sockets in the returned socket list and move them
|
||||
* to the start of the list.
|
||||
*/
|
||||
for (i = num_local_xsmp_sockets = 0; i < num_xsmp_sockets; i++)
|
||||
{
|
||||
char *id = IceGetListenConnectionString (xsmp_sockets[i]);
|
||||
|
||||
if (!strncmp (id, "local/", sizeof ("local/") - 1) ||
|
||||
!strncmp (id, "unix/", sizeof ("unix/") - 1))
|
||||
{
|
||||
if (i > num_local_xsmp_sockets)
|
||||
{
|
||||
IceListenObj tmp = xsmp_sockets[i];
|
||||
xsmp_sockets[i] = xsmp_sockets[num_local_xsmp_sockets];
|
||||
xsmp_sockets[num_local_xsmp_sockets] = tmp;
|
||||
}
|
||||
num_local_xsmp_sockets++;
|
||||
}
|
||||
free (id);
|
||||
}
|
||||
|
||||
if (num_local_xsmp_sockets == 0)
|
||||
g_error ("IceListenForConnections did not return a local listener!");
|
||||
|
||||
#ifdef HAVE_X11_XTRANS_XTRANS_H
|
||||
if (num_local_xsmp_sockets != num_xsmp_sockets)
|
||||
{
|
||||
/* Xtrans was apparently compiled with support for some
|
||||
* non-local transport besides TCP (which we disabled above); we
|
||||
* won't create IO watches on those extra sockets, so
|
||||
* connections to them will never be noticed, but they're still
|
||||
* there, which is inelegant.
|
||||
*
|
||||
* If the g_warning below is triggering for you and you want to
|
||||
* stop it, the fix is to add additional _IceTransNoListen()
|
||||
* calls above.
|
||||
*/
|
||||
network_id_list =
|
||||
IceComposeNetworkIdList (num_xsmp_sockets - num_local_xsmp_sockets,
|
||||
xsmp_sockets + num_local_xsmp_sockets);
|
||||
g_warning ("IceListenForConnections returned %d non-local listeners: %s",
|
||||
num_xsmp_sockets - num_local_xsmp_sockets, network_id_list);
|
||||
free (network_id_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update .ICEauthority with new auth entries for our socket */
|
||||
if (!update_iceauthority (TRUE))
|
||||
{
|
||||
/* FIXME: is this really fatal? Hm... */
|
||||
g_error ("Could not update ICEauthority file %s",
|
||||
IceAuthFileName ());
|
||||
}
|
||||
|
||||
network_id_list = IceComposeNetworkIdList (num_local_xsmp_sockets,
|
||||
xsmp_sockets);
|
||||
|
||||
return network_id_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_xsmp_run:
|
||||
*
|
||||
* Sets the XSMP server to start accepting connections.
|
||||
**/
|
||||
void
|
||||
gsm_xsmp_run (void)
|
||||
{
|
||||
GIOChannel *channel;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_local_xsmp_sockets; i++)
|
||||
{
|
||||
channel = g_io_channel_unix_new (IceGetListenConnectionNumber (xsmp_sockets[i]));
|
||||
g_io_add_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR,
|
||||
accept_ice_connection, xsmp_sockets[i]);
|
||||
g_io_channel_unref (channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_xsmp_shutdown:
|
||||
*
|
||||
* Shuts down the XSMP server and closes the ICE listening socket
|
||||
**/
|
||||
void
|
||||
gsm_xsmp_shutdown (void)
|
||||
{
|
||||
update_iceauthority (FALSE);
|
||||
|
||||
IceFreeListenObjs (num_xsmp_sockets, xsmp_sockets);
|
||||
xsmp_sockets = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsm_xsmp_generate_client_id:
|
||||
*
|
||||
* Generates a new XSMP client ID.
|
||||
*
|
||||
* Return value: an XSMP client ID.
|
||||
**/
|
||||
char *
|
||||
gsm_xsmp_generate_client_id (void)
|
||||
{
|
||||
static int sequence = -1;
|
||||
static guint rand1 = 0, rand2 = 0;
|
||||
static pid_t pid = 0;
|
||||
struct timeval tv;
|
||||
|
||||
/* The XSMP spec defines the ID as:
|
||||
*
|
||||
* Version: "1"
|
||||
* Address type and address:
|
||||
* "1" + an IPv4 address as 8 hex digits
|
||||
* "2" + a DECNET address as 12 hex digits
|
||||
* "6" + an IPv6 address as 32 hex digits
|
||||
* Time stamp: milliseconds since UNIX epoch as 13 decimal digits
|
||||
* Process-ID type and process-ID:
|
||||
* "1" + POSIX PID as 10 decimal digits
|
||||
* Sequence number as 4 decimal digits
|
||||
*
|
||||
* XSMP client IDs are supposed to be globally unique: if
|
||||
* SmsGenerateClientID() is unable to determine a network
|
||||
* address for the machine, it gives up and returns %NULL.
|
||||
* GNOME and KDE have traditionally used a fourth address
|
||||
* format in this case:
|
||||
* "0" + 16 random hex digits
|
||||
*
|
||||
* We don't even bother trying SmsGenerateClientID(), since the
|
||||
* user's IP address is probably "192.168.1.*" anyway, so a random
|
||||
* number is actually more likely to be globally unique.
|
||||
*/
|
||||
|
||||
if (!rand1)
|
||||
{
|
||||
rand1 = g_random_int ();
|
||||
rand2 = g_random_int ();
|
||||
pid = getpid ();
|
||||
}
|
||||
|
||||
sequence = (sequence + 1) % 10000;
|
||||
gettimeofday (&tv, NULL);
|
||||
return g_strdup_printf ("10%.04x%.04x%.10lu%.3u%.10lu%.4d",
|
||||
rand1, rand2,
|
||||
(unsigned long) tv.tv_sec,
|
||||
(unsigned) tv.tv_usec,
|
||||
(unsigned long) pid,
|
||||
sequence);
|
||||
}
|
||||
|
||||
/* This is called (by glib via xsmp->ice_connection_watch) when a
|
||||
* connection is first received on the ICE listening socket. (We
|
||||
* expect that the client will then initiate XSMP on the connection;
|
||||
* if it does not, GsmClientXSMP will eventually time out and close
|
||||
* the connection.)
|
||||
*
|
||||
* FIXME: it would probably make more sense to not create a
|
||||
* GsmClientXSMP object until accept_xsmp_connection, below (and to do
|
||||
* the timing-out here in xsmp.c).
|
||||
*/
|
||||
static gboolean
|
||||
accept_ice_connection (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
IceListenObj listener = data;
|
||||
IceConn ice_conn;
|
||||
IceAcceptStatus status;
|
||||
GsmClientXSMP *client;
|
||||
|
||||
g_debug ("accept_ice_connection()");
|
||||
|
||||
ice_conn = IceAcceptConnection (listener, &status);
|
||||
if (status != IceAcceptSuccess)
|
||||
{
|
||||
g_debug ("IceAcceptConnection returned %d", status);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
client = gsm_client_xsmp_new (ice_conn);
|
||||
ice_conn->context = client;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* This is called (by libSM) when XSMP is initiated on an ICE
|
||||
* connection that was already accepted by accept_ice_connection.
|
||||
*/
|
||||
static Status
|
||||
accept_xsmp_connection (SmsConn sms_conn, SmPointer manager_data,
|
||||
unsigned long *mask_ret, SmsCallbacks *callbacks_ret,
|
||||
char **failure_reason_ret)
|
||||
{
|
||||
IceConn ice_conn;
|
||||
GsmClientXSMP *client;
|
||||
|
||||
/* FIXME: what about during shutdown but before gsm_xsmp_shutdown? */
|
||||
if (!xsmp_sockets)
|
||||
{
|
||||
g_debug ("In shutdown, rejecting new client");
|
||||
|
||||
*failure_reason_ret =
|
||||
strdup (_("Refusing new client connection because the session is currently being shut down\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ice_conn = SmsGetIceConnection (sms_conn);
|
||||
client = ice_conn->context;
|
||||
|
||||
g_return_val_if_fail (client != NULL, TRUE);
|
||||
|
||||
gsm_client_xsmp_connect (client, sms_conn, mask_ret, callbacks_ret);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ICEauthority stuff */
|
||||
|
||||
/* Various magic numbers stolen from iceauth.c */
|
||||
#define GSM_ICE_AUTH_RETRIES 10
|
||||
#define GSM_ICE_AUTH_INTERVAL 2 /* 2 seconds */
|
||||
#define GSM_ICE_AUTH_LOCK_TIMEOUT 600 /* 10 minutes */
|
||||
|
||||
#define GSM_ICE_MAGIC_COOKIE_AUTH_NAME "MIT-MAGIC-COOKIE-1"
|
||||
#define GSM_ICE_MAGIC_COOKIE_LEN 16
|
||||
|
||||
static IceAuthFileEntry *
|
||||
auth_entry_new (const char *protocol, const char *network_id)
|
||||
{
|
||||
IceAuthFileEntry *file_entry;
|
||||
IceAuthDataEntry data_entry;
|
||||
|
||||
file_entry = malloc (sizeof (IceAuthFileEntry));
|
||||
|
||||
file_entry->protocol_name = strdup (protocol);
|
||||
file_entry->protocol_data = NULL;
|
||||
file_entry->protocol_data_length = 0;
|
||||
file_entry->network_id = strdup (network_id);
|
||||
file_entry->auth_name = strdup (GSM_ICE_MAGIC_COOKIE_AUTH_NAME);
|
||||
file_entry->auth_data = IceGenerateMagicCookie (GSM_ICE_MAGIC_COOKIE_LEN);
|
||||
file_entry->auth_data_length = GSM_ICE_MAGIC_COOKIE_LEN;
|
||||
|
||||
/* Also create an in-memory copy, which is what the server will
|
||||
* actually use for checking client auth.
|
||||
*/
|
||||
data_entry.protocol_name = file_entry->protocol_name;
|
||||
data_entry.network_id = file_entry->network_id;
|
||||
data_entry.auth_name = file_entry->auth_name;
|
||||
data_entry.auth_data = file_entry->auth_data;
|
||||
data_entry.auth_data_length = file_entry->auth_data_length;
|
||||
IceSetPaAuthData (1, &data_entry);
|
||||
|
||||
return file_entry;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_iceauthority (gboolean adding)
|
||||
{
|
||||
char *filename = IceAuthFileName ();
|
||||
char **our_network_ids;
|
||||
FILE *fp;
|
||||
IceAuthFileEntry *auth_entry;
|
||||
GSList *entries, *e;
|
||||
int i;
|
||||
gboolean ok = FALSE;
|
||||
|
||||
if (IceLockAuthFile (filename, GSM_ICE_AUTH_RETRIES, GSM_ICE_AUTH_INTERVAL,
|
||||
GSM_ICE_AUTH_LOCK_TIMEOUT) != IceAuthLockSuccess)
|
||||
return FALSE;
|
||||
|
||||
our_network_ids = g_malloc (num_local_xsmp_sockets * sizeof (char *));
|
||||
for (i = 0; i < num_local_xsmp_sockets; i++)
|
||||
our_network_ids[i] = IceGetListenConnectionString (xsmp_sockets[i]);
|
||||
|
||||
entries = NULL;
|
||||
|
||||
fp = fopen (filename, "r+");
|
||||
if (fp)
|
||||
{
|
||||
while ((auth_entry = IceReadAuthFileEntry (fp)) != NULL)
|
||||
{
|
||||
/* Skip/delete entries with no network ID (invalid), or with
|
||||
* our network ID; if we're starting up, an entry with our
|
||||
* ID must be a stale entry left behind by an old process,
|
||||
* and if we're shutting down, it won't be valid in the
|
||||
* future, so either way we want to remove it from the list.
|
||||
*/
|
||||
if (!auth_entry->network_id)
|
||||
{
|
||||
IceFreeAuthFileEntry (auth_entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_local_xsmp_sockets; i++)
|
||||
{
|
||||
if (!strcmp (auth_entry->network_id, our_network_ids[i]))
|
||||
{
|
||||
IceFreeAuthFileEntry (auth_entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != num_local_xsmp_sockets)
|
||||
continue;
|
||||
|
||||
entries = g_slist_prepend (entries, auth_entry);
|
||||
}
|
||||
|
||||
rewind (fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (g_file_test (filename, G_FILE_TEST_EXISTS))
|
||||
{
|
||||
g_warning ("Unable to read ICE authority file: %s", filename);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fd = open (filename, O_CREAT | O_WRONLY, 0600);
|
||||
fp = fdopen (fd, "w");
|
||||
if (!fp)
|
||||
{
|
||||
g_warning ("Unable to write to ICE authority file: %s", filename);
|
||||
if (fd != -1)
|
||||
close (fd);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (adding)
|
||||
{
|
||||
for (i = 0; i < num_local_xsmp_sockets; i++)
|
||||
{
|
||||
entries = g_slist_append (entries,
|
||||
auth_entry_new ("ICE", our_network_ids[i]));
|
||||
entries = g_slist_prepend (entries,
|
||||
auth_entry_new ("XSMP", our_network_ids[i]));
|
||||
}
|
||||
}
|
||||
|
||||
for (e = entries; e; e = e->next)
|
||||
{
|
||||
IceAuthFileEntry *auth_entry = e->data;
|
||||
IceWriteAuthFileEntry (fp, auth_entry);
|
||||
IceFreeAuthFileEntry (auth_entry);
|
||||
}
|
||||
g_slist_free (entries);
|
||||
|
||||
fclose (fp);
|
||||
ok = TRUE;
|
||||
|
||||
cleanup:
|
||||
IceUnlockAuthFile (filename);
|
||||
for (i = 0; i < num_local_xsmp_sockets; i++)
|
||||
free (our_network_ids[i]);
|
||||
g_free (our_network_ids);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Error handlers */
|
||||
|
||||
static void
|
||||
ice_error_handler (IceConn conn, Bool swap, int offending_minor_opcode,
|
||||
unsigned long offending_sequence, int error_class,
|
||||
int severity, IcePointer values)
|
||||
{
|
||||
g_debug ("ice_error_handler (%p, %s, %d, %lx, %d, %d)",
|
||||
conn, swap ? "TRUE" : "FALSE", offending_minor_opcode,
|
||||
offending_sequence, error_class, severity);
|
||||
|
||||
if (severity == IceCanContinue)
|
||||
return;
|
||||
|
||||
/* FIXME: the ICElib docs are completely vague about what we're
|
||||
* supposed to do in this case. Need to verify that calling
|
||||
* IceCloseConnection() here is guaranteed to cause neither
|
||||
* free-memory-reads nor leaks.
|
||||
*/
|
||||
IceCloseConnection (conn);
|
||||
}
|
||||
|
||||
static void
|
||||
ice_io_error_handler (IceConn conn)
|
||||
{
|
||||
g_debug ("ice_io_error_handler (%p)", conn);
|
||||
|
||||
/* We don't need to do anything here; the next call to
|
||||
* IceProcessMessages() for this connection will receive
|
||||
* IceProcessMessagesIOError and we can handle the error there.
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
sms_error_handler (SmsConn conn, Bool swap, int offending_minor_opcode,
|
||||
unsigned long offending_sequence_num, int error_class,
|
||||
int severity, IcePointer values)
|
||||
{
|
||||
g_debug ("sms_error_handler (%p, %s, %d, %lx, %d, %d)",
|
||||
conn, swap ? "TRUE" : "FALSE", offending_minor_opcode,
|
||||
offending_sequence_num, error_class, severity);
|
||||
|
||||
/* We don't need to do anything here; if the connection needs to be
|
||||
* closed, libSM will do that itself.
|
||||
*/
|
||||
}
|
29
src/sugar/xsmp.h
Normal file
29
src/sugar/xsmp.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* xsmp.h
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GSM_XSMP_H__
|
||||
#define __GSM_XSMP_H__
|
||||
|
||||
char *gsm_xsmp_init (void);
|
||||
void gsm_xsmp_run (void);
|
||||
void gsm_xsmp_shutdown (void);
|
||||
|
||||
char *gsm_xsmp_generate_client_id (void);
|
||||
|
||||
#endif /* __GSM_XSMP_H__ */
|
Loading…
Reference in New Issue
Block a user