From ea1fecfeb3f416a039b16d9bf66414b4900f4cba Mon Sep 17 00:00:00 2001 From: Brian Cameron Date: Thu, 15 Dec 2011 20:28:26 -0600 Subject: Add ConsoleKit MultiSeat support. See bug 19333. --- configure.ac | 22 + data/00-primary.seat | 23 +- data/ConsoleKit.conf | 32 +- data/Makefile.am | 5 + data/displays.d/Headless.display.in | 5 + data/displays.d/Local.display.in | 5 + data/displays.d/LocalVNC.display.in | 5 + data/displays.d/Makefile.am | 29 + data/displays.d/RemoteMachine.display.in | 5 + data/sessions.d/Headless.session | 8 + data/sessions.d/Local.session | 9 + data/sessions.d/LocalVNC.session | 8 + data/sessions.d/Makefile.am | 16 + data/sessions.d/Remote.session | 9 + doc/dbus/ck-terms.xml | 7 + libck-connector/ck-connector.c | 3 + src/Makefile.am | 9 + src/ck-display-template.c | 341 ++++++++++++ src/ck-display-template.h | 57 ++ src/ck-log-event.c | 18 +- src/ck-log-event.h | 5 + src/ck-manager.c | 710 +++++++++++++++++++++---- src/ck-manager.h | 31 +- src/ck-marshal.list | 3 + src/ck-seat.c | 818 +++++++++++++++++++++++++++-- src/ck-seat.h | 50 +- src/ck-session-leader.c | 1 + src/ck-session.c | 574 +++++++++++++++++++- src/ck-session.h | 46 +- src/org.freedesktop.ConsoleKit.Manager.xml | 136 +++++ src/org.freedesktop.ConsoleKit.Seat.xml | 86 +++ src/org.freedesktop.ConsoleKit.Session.xml | 92 ++++ src/strverscmp.c | 131 +++++ src/strverscmp.h | 25 + tools/Makefile.am | 9 + tools/ck-seat-tool.c | 449 ++++++++++++++++ tools/list-sessions.c | 94 +++- 37 files changed, 3684 insertions(+), 192 deletions(-) create mode 100644 data/displays.d/Headless.display.in create mode 100644 data/displays.d/Local.display.in create mode 100644 data/displays.d/LocalVNC.display.in create mode 100644 data/displays.d/Makefile.am create mode 100644 data/displays.d/RemoteMachine.display.in create mode 100644 data/sessions.d/Headless.session create mode 100644 data/sessions.d/Local.session create mode 100644 data/sessions.d/LocalVNC.session create mode 100644 data/sessions.d/Makefile.am create mode 100644 data/sessions.d/Remote.session create mode 100644 src/ck-display-template.c create mode 100644 src/ck-display-template.h create mode 100644 src/strverscmp.c create mode 100644 src/strverscmp.h create mode 100644 tools/ck-seat-tool.c diff --git a/configure.ac b/configure.ac index 7394ed4..0b81434 100644 --- a/configure.ac +++ b/configure.ac @@ -202,6 +202,18 @@ AM_CONDITIONAL(CK_COMPILE_FREEBSD, test x$CK_BACKEND = xfreebsd, [Compiling for AM_CONDITIONAL(CK_COMPILE_SOLARIS, test x$CK_BACKEND = xsolaris, [Compiling for Solaris]) AC_SUBST(CK_BACKEND) +dnl --------------------------------------------------------------------------- +dnl Check for X11 DIR +dnl --------------------------------------------------------------------------- + +X11_DIR=`$PKG_CONFIG --variable=bindir xorg-server 2>/dev/null` +if test "x$X11_DIR" = x; then + AC_PATH_PROGS([XSERVER], [Xorg X],,[$PATH:/usr/X11/bin:/usr/bin]) + test "x$XSERVER" != x && X11_DIR=`dirname "$XSERVER"` +fi +test "x$X11_DIR" = x && X11_DIR=$bindir +AC_SUBST([X11_DIR]) + dnl --------------------------------------------------------------------------- dnl Check for PAM dnl --------------------------------------------------------------------------- @@ -302,6 +314,14 @@ if test "x$enable_inotify" = "xyes" ; then AC_DEFINE(ENABLE_INOTIFY, [], [Define if we have inotify]) fi +dnl --------------------------------------------------------------------------- +dnl check for strverscmp +dnl --------------------------------------------------------------------------- +have_strverscmp=no +AC_CHECK_FUNCS(strverscmp, [have_strverscmp=yes], []) + +AM_CONDITIONAL(USE_SELF_STRVERSCMP, test "x$have_strverscmp" = "xno", [Define if we do not have strverscmp]) + dnl --------------------------------------------------------------------------- dnl check for RBAC dnl --------------------------------------------------------------------------- @@ -410,6 +430,8 @@ tools/linux/Makefile tools/freebsd/Makefile tools/solaris/Makefile data/Makefile +data/displays.d/Makefile +data/sessions.d/Makefile doc/Makefile doc/dbus/ConsoleKit.xml doc/dbus/Makefile diff --git a/data/00-primary.seat b/data/00-primary.seat index 6e61db4..0632382 100644 --- a/data/00-primary.seat +++ b/data/00-primary.seat @@ -1,5 +1,26 @@ [Seat Entry] Version=1.0 Name=Primary seat +# Specified Seat ID, if this value is NULL, ConsoleKit will decide one. +# The ID only contain the ASICC characters "[A-Z][a-z][0-9]_" +ID=StaticSeat1 +Description=start one static local display at :0 + +# Indicate whether to create this seat or not. If it is set true, then CK will +# not create this seat. Default value is false. Hidden=false -Devices= \ No newline at end of file + +# Indicate input/output devices including keyboard-pointer-video +# card-monitor-sound-usb devices, +# This key will not implemented now, it might need be divided into +# several keys in the future: +# Pointer= +# Monitor= +# VideoCard= +# Monitor= +# UsbHub= +Devices= + +# List of sessions to start on the seat, separated by ';' +# Each session is defined in sessions.d/ +Sessions=Local; diff --git a/data/ConsoleKit.conf b/data/ConsoleKit.conf index 948f95f..8cf490a 100644 --- a/data/ConsoleKit.conf +++ b/data/ConsoleKit.conf @@ -42,6 +42,9 @@ + @@ -69,6 +72,18 @@ + + + + + + @@ -125,6 +146,12 @@ + + @@ -134,14 +161,13 @@ - + diff --git a/data/Makefile.am b/data/Makefile.am index 248f7cd..9f11bad 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,5 +1,10 @@ NULL = +SUBDIRS = \ + displays.d \ + sessions.d \ + $(NULL) + dbusconfdir = $(DBUS_SYS_DIR) dbusconf_DATA = ConsoleKit.conf diff --git a/data/displays.d/Headless.display.in b/data/displays.d/Headless.display.in new file mode 100644 index 0000000..754d2bf --- /dev/null +++ b/data/displays.d/Headless.display.in @@ -0,0 +1,5 @@ +[Display] +Type=X11 + +[X11] +Exec=@X11_DIR@/Xvfb $display -auth $auth diff --git a/data/displays.d/Local.display.in b/data/displays.d/Local.display.in new file mode 100644 index 0000000..b845a7b --- /dev/null +++ b/data/displays.d/Local.display.in @@ -0,0 +1,5 @@ +[Display] +Type=X11 + +[X11] +Exec=@X11_DIR@/Xorg $display -br -verbose -auth $auth -nolisten tcp $vt diff --git a/data/displays.d/LocalVNC.display.in b/data/displays.d/LocalVNC.display.in new file mode 100644 index 0000000..6ad336b --- /dev/null +++ b/data/displays.d/LocalVNC.display.in @@ -0,0 +1,5 @@ +[Display] +Type=X11 + +[X11] +Exec=@X11_DIR@/Xvnc $display -auth $auth -query localhost diff --git a/data/displays.d/Makefile.am b/data/displays.d/Makefile.am new file mode 100644 index 0000000..1fab1d2 --- /dev/null +++ b/data/displays.d/Makefile.am @@ -0,0 +1,29 @@ +NULL = + +displaydir = $(sysconfdir)/ConsoleKit/displays.d +display_in_files = \ + Local.display.in \ + RemoteMachine.display.in \ + LocalVNC.display.in \ + Headless.display.in + +display_DATA = $(display_in_files:.display.in=.display) + +Local.display: Local.display.in Makefile + sed -e "s|\@X11_DIR\@|$(X11_DIR)|" $< > $@ +RemoteMachine.display: RemoteMachine.display.in Makefile + sed -e "s|\@X11_DIR\@|$(X11_DIR)|" $< > $@ +LocalVNC.display: LocalVNC.display.in Makefile + sed -e "s|\@X11_DIR\@|$(X11_DIR)|" $< > $@ +Headless.display: Headless.display.in Makefile + sed -e "s|\@X11_DIR\@|$(X11_DIR)|" $< > $@ + +EXTRA_DIST = \ + $(display_in_files) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in + +CLEANFILES = $(display_DATA) diff --git a/data/displays.d/RemoteMachine.display.in b/data/displays.d/RemoteMachine.display.in new file mode 100644 index 0000000..7c69451 --- /dev/null +++ b/data/displays.d/RemoteMachine.display.in @@ -0,0 +1,5 @@ +[Display] +Type=X11 + +[X11] +Exec=@X11_DIR@/Xorg $display -br -verbose -auth $auth -indirect $vt diff --git a/data/sessions.d/Headless.session b/data/sessions.d/Headless.session new file mode 100644 index 0000000..d376af9 --- /dev/null +++ b/data/sessions.d/Headless.session @@ -0,0 +1,8 @@ +[Session Entry] +Name=Headless +Type=LoginWindow +Description=Login Window running on headless display +DisplayTemplate=Headless + +[Headless] +display=:32 diff --git a/data/sessions.d/Local.session b/data/sessions.d/Local.session new file mode 100644 index 0000000..9d3b975 --- /dev/null +++ b/data/sessions.d/Local.session @@ -0,0 +1,9 @@ +[Session Entry] +Name=Local +Type=LoginWindow +Description=Local Login Screen +DisplayTemplate=Local + +[Local] +display=:0 +vt=vt1 diff --git a/data/sessions.d/LocalVNC.session b/data/sessions.d/LocalVNC.session new file mode 100644 index 0000000..c05802f --- /dev/null +++ b/data/sessions.d/LocalVNC.session @@ -0,0 +1,8 @@ +[Session Entry] +Name=LocalVNC +Type=LoginWindow +Description=Connect to local VNC server running on same machine +DisplayTemplate=LocalVNC + +[LocalVNC] +display=:64 diff --git a/data/sessions.d/Makefile.am b/data/sessions.d/Makefile.am new file mode 100644 index 0000000..f17ffdc --- /dev/null +++ b/data/sessions.d/Makefile.am @@ -0,0 +1,16 @@ +NULL = + +sessiondir = $(sysconfdir)/ConsoleKit/sessions.d +session_DATA = \ + Headless.session \ + Local.session \ + LocalVNC.session \ + Remote.session + +EXTRA_DIST = \ + $(session_DATA) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in diff --git a/data/sessions.d/Remote.session b/data/sessions.d/Remote.session new file mode 100644 index 0000000..e88f975 --- /dev/null +++ b/data/sessions.d/Remote.session @@ -0,0 +1,9 @@ +[Session Entry] +Name=Remote Chooser +Type=Remote +Description=Connect to chooser on nearby remote machine +DisplayTemplate=RemoteMachine + +[RemoteMachine] +display=:96 +vt=vt10 diff --git a/doc/dbus/ck-terms.xml b/doc/dbus/ck-terms.xml index d3d544d..1b43ca6 100644 --- a/doc/dbus/ck-terms.xml +++ b/doc/dbus/ck-terms.xml @@ -64,4 +64,11 @@ True, hardware, multi-seat capabilities will be added in a later release. + + Seat manager + +The seat manager is the process responsible for starting and stopping sessions on a seat. + + + diff --git a/libck-connector/ck-connector.c b/libck-connector/ck-connector.c index 7f6f87f..87a7d4a 100644 --- a/libck-connector/ck-connector.c +++ b/libck-connector/ck-connector.c @@ -76,8 +76,11 @@ static struct { { "display-device", DBUS_TYPE_STRING }, { "x11-display-device", DBUS_TYPE_STRING }, { "x11-display", DBUS_TYPE_STRING }, + { "seat-id", DBUS_TYPE_STRING }, + { "session", DBUS_TYPE_STRING }, { "remote-host-name", DBUS_TYPE_STRING }, { "session-type", DBUS_TYPE_STRING }, + { "display-type", DBUS_TYPE_STRING }, { "is-local", DBUS_TYPE_BOOLEAN }, { "unix-user", DBUS_TYPE_INT32 }, }; diff --git a/src/Makefile.am b/src/Makefile.am index 869decd..8d0bca5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -108,6 +108,8 @@ console_kit_daemon_SOURCES = \ ck-file-monitor.h \ ck-job.h \ ck-job.c \ + ck-display-template.h \ + ck-display-template.c \ ck-seat.h \ ck-seat.c \ ck-session-leader.h \ @@ -123,6 +125,13 @@ console_kit_daemon_SOURCES = \ $(BUILT_SOURCES) \ $(NULL) +if USE_SELF_STRVERSCMP +console_kit_daemon_SOURCES += \ + strverscmp.c \ + strverscmp.h \ + $(NULL) +endif + if ENABLE_INOTIFY FILE_MONITOR_BACKEND = ck-file-monitor-inotify.c else diff --git a/src/ck-display-template.c b/src/ck-display-template.c new file mode 100644 index 0000000..9206103 --- /dev/null +++ b/src/ck-display-template.c @@ -0,0 +1,341 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Authors: halton.huo@sun.com, Ray Strode + * Copyright (C) 2009 Sun Microsystems, Inc. + * Red Hat, 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 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 "config.h" + +#include +#include +#include + +#include "ck-display-template.h" + +#define CK_DISPLAY_TEMPLATE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_DISPLAY_TEMPLATE, CkDisplayTemplatePrivate)) + +#define CK_DISPLAY_TEMPLATES_DIR SYSCONFDIR "/ConsoleKit/displays.d" + +struct CkDisplayTemplatePrivate +{ + char *name; + char *type; + GHashTable *parameters; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_TYPE, + PROP_PARAMETERS, +}; + +static void ck_display_template_class_init (CkDisplayTemplateClass *klass); +static void ck_display_template_init (CkDisplayTemplate *display); +static void ck_display_template_finalize (GObject *object); +static gboolean ck_display_template_load (CkDisplayTemplate *display); + +static GHashTable *ck_display_templates; + +G_DEFINE_TYPE (CkDisplayTemplate, ck_display_template, G_TYPE_OBJECT) + +static void +_ck_display_template_set_name (CkDisplayTemplate *display, + const char *name) +{ + g_free (display->priv->name); + display->priv->name = g_strdup (name); +} + +static void +_ck_display_template_set_type_string (CkDisplayTemplate *display, + const char *type) +{ + g_free (display->priv->type); + display->priv->type = g_strdup (type); +} + +static void +_ck_display_template_set_parameters (CkDisplayTemplate *display, + GHashTable *parameters) +{ + if (display->priv->parameters != NULL) { + g_hash_table_unref (display->priv->parameters); + } + + if (parameters == NULL) { + display->priv->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + } else { + display->priv->parameters = g_hash_table_ref (parameters); + } +} + +static void +ck_display_template_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CkDisplayTemplate *self; + + self = CK_DISPLAY_TEMPLATE (object); + + switch (prop_id) { + case PROP_NAME: + _ck_display_template_set_name (self, g_value_get_string (value)); + break; + case PROP_TYPE: + _ck_display_template_set_type_string (self, g_value_get_string (value)); + break; + case PROP_PARAMETERS: + _ck_display_template_set_parameters (self, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_display_template_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CkDisplayTemplate *self; + + self = CK_DISPLAY_TEMPLATE (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, self->priv->name); + break; + case PROP_TYPE: + g_value_set_string (value, self->priv->type); + break; + case PROP_PARAMETERS: + g_value_set_boxed (value, self->priv->parameters); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_display_template_class_init (CkDisplayTemplateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = ck_display_template_get_property; + object_class->set_property = ck_display_template_set_property; + object_class->finalize = ck_display_template_finalize; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "display type name", + "display type name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_TYPE, + g_param_spec_string ("type", + "type", + "Type", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_PARAMETERS, + g_param_spec_boxed ("parameters", + "Parameters", + "Parameters", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_type_class_add_private (klass, sizeof (CkDisplayTemplatePrivate)); +} + +static void +ck_display_template_init (CkDisplayTemplate *display) +{ + display->priv = CK_DISPLAY_TEMPLATE_GET_PRIVATE (display); + + display->priv->name = NULL; + display->priv->type = NULL; + display->priv->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); +} + +static void +ck_display_template_finalize (GObject *object) +{ + CkDisplayTemplate *display; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_DISPLAY_TEMPLATE (object)); + + display = CK_DISPLAY_TEMPLATE (object); + + g_return_if_fail (display->priv != NULL); + + g_free (display->priv->name); + g_free (display->priv->type); + g_hash_table_unref (display->priv->parameters); + + G_OBJECT_CLASS (ck_display_template_parent_class)->finalize (object); +} + +static gboolean +ck_display_template_load (CkDisplayTemplate *display) +{ + GKeyFile *key_file; + const char *name; + char *group; + char *filename; + gboolean hidden; + char *type; + gboolean res; + GError *error; + char **type_keys; + GHashTable *parameters; + + name = ck_display_template_get_name (display); + + g_return_val_if_fail (name && !g_str_equal (name, ""), FALSE); + + filename = g_strdup_printf ("%s/%s.display", CK_DISPLAY_TEMPLATES_DIR, name); + + key_file = g_key_file_new (); + + error = NULL; + res = g_key_file_load_from_file (key_file, + filename, + G_KEY_FILE_NONE, + &error); + if (! res) { + g_warning ("Unable to load display from file %s: %s", filename, error->message); + g_error_free (error); + return FALSE; + } + g_free (filename); + + group = g_key_file_get_start_group (key_file); + + if (group == NULL || strcmp (group, "Display") != 0) { + g_warning ("Not a display type file: %s", filename); + g_free (group); + g_key_file_free (key_file); + return FALSE; + } + + hidden = g_key_file_get_boolean (key_file, group, "Hidden", NULL); + + type = g_key_file_get_string (key_file, group, "Type", NULL); + + if (type == NULL) { + g_warning ("Unable to read type from display file"); + g_free (group); + g_key_file_free (key_file); + return FALSE; + } + + display->priv->type = type; + + parameters = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + + type_keys = g_key_file_get_keys (key_file, type, NULL, NULL); + + if (type_keys != NULL) { + int i; + for (i = 0; type_keys[i] != NULL; i++) { + char *string; + + string = g_key_file_get_string (key_file, type, type_keys[i], NULL); + g_hash_table_insert (parameters, g_strdup (type_keys[i]), string); + } + g_strfreev (type_keys); + } + + _ck_display_template_set_parameters (display, parameters); + g_hash_table_unref (parameters); + + g_free (group); + g_key_file_free (key_file); + return TRUE; +} + +CkDisplayTemplate * +ck_display_template_get_from_name (const char *name) +{ + CkDisplayTemplate *display_template; + + if (ck_display_templates == NULL) { + ck_display_templates = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + } + + display_template = g_hash_table_lookup (ck_display_templates, name); + + if (display_template == NULL) { + GObject *object; + + object = g_object_new (CK_TYPE_DISPLAY_TEMPLATE, + "name", name, + NULL); + + if (!ck_display_template_load (CK_DISPLAY_TEMPLATE (object))) { + g_object_unref (object); + return NULL; + } + + g_hash_table_insert (ck_display_templates, g_strdup (name), object); + display_template = CK_DISPLAY_TEMPLATE (object); + } + + return g_object_ref (display_template); +} + +G_CONST_RETURN char* +ck_display_template_get_name (CkDisplayTemplate *display) +{ + g_return_val_if_fail (CK_IS_DISPLAY_TEMPLATE (display), NULL); + + return display->priv->name; +} + +G_CONST_RETURN char * +ck_display_template_get_type_string (CkDisplayTemplate *display) +{ + return display->priv->type; +} + +GHashTable * +ck_display_template_get_parameters (CkDisplayTemplate *display) +{ + g_return_val_if_fail (CK_IS_DISPLAY_TEMPLATE (display), NULL); + + return g_hash_table_ref (display->priv->parameters); +} + diff --git a/src/ck-display-template.h b/src/ck-display-template.h new file mode 100644 index 0000000..fa74e67 --- /dev/null +++ b/src/ck-display-template.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Authors: halton.huo@sun.com + * Copyright (C) 2009 Sun Microsystems, 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 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 __CK_DISPLAY_TEMPLATE_H +#define __CK_DISPLAY_TEMPLATE_H + +#include + +G_BEGIN_DECLS + +#define CK_TYPE_DISPLAY_TEMPLATE (ck_display_template_get_type ()) +#define CK_DISPLAY_TEMPLATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_DISPLAY_TEMPLATE, CkDisplayTemplate)) +#define CK_DISPLAY_TEMPLATE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_DISPLAY_TEMPLATE, CkDisplayTemplateClass)) +#define CK_IS_DISPLAY_TEMPLATE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_DISPLAY_TEMPLATE)) +#define CK_IS_DISPLAY_TEMPLATE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_DISPLAY_TEMPLATE)) +#define CK_DISPLAY_TEMPLATE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_DISPLAY_TEMPLATE, CkDisplayTemplateClass)) + +typedef struct CkDisplayTemplatePrivate CkDisplayTemplatePrivate; + +typedef struct +{ + GObject parent; + CkDisplayTemplatePrivate *priv; +} CkDisplayTemplate; + +typedef struct +{ + GObjectClass parent_class; +} CkDisplayTemplateClass; + +GType ck_display_template_get_type (void); +CkDisplayTemplate * ck_display_template_get_from_name (const char *name); +G_CONST_RETURN char * ck_display_template_get_name (CkDisplayTemplate *display); +G_CONST_RETURN char * ck_display_template_get_type_string (CkDisplayTemplate *display); +GHashTable * ck_display_template_get_parameters (CkDisplayTemplate *display); + +G_END_DECLS + +#endif /* __CK_DISPLAY_TEMPLATE_H */ diff --git a/src/ck-log-event.c b/src/ck-log-event.c index 5cc88ce..d13a0f8 100644 --- a/src/ck-log-event.c +++ b/src/ck-log-event.c @@ -79,6 +79,8 @@ event_seat_session_added_free (CkLogSeatSessionAddedEvent *event) event->session_id = NULL; g_free (event->session_type); event->session_type = NULL; + g_free (event->display_type); + event->display_type = NULL; g_free (event->session_x11_display); event->session_x11_display = NULL; g_free (event->session_x11_display_device); @@ -103,6 +105,8 @@ event_seat_session_removed_free (CkLogSeatSessionRemovedEvent *event) event->session_id = NULL; g_free (event->session_type); event->session_type = NULL; + g_free (event->display_type); + event->display_type = NULL; g_free (event->session_x11_display); event->session_x11_display = NULL; g_free (event->session_x11_display_device); @@ -213,6 +217,7 @@ event_seat_session_added_copy (CkLogSeatSessionAddedEvent *event, event_copy->seat_id = g_strdup (event->seat_id); event_copy->session_id = g_strdup (event->session_id); event_copy->session_type = g_strdup (event->session_type); + event_copy->display_type = g_strdup (event->display_type); event_copy->session_x11_display = g_strdup (event->session_x11_display); event_copy->session_x11_display_device = g_strdup (event->session_x11_display_device); event_copy->session_display_device = g_strdup (event->session_display_device); @@ -232,6 +237,7 @@ event_seat_session_removed_copy (CkLogSeatSessionRemovedEvent *event, event_copy->seat_id = g_strdup (event->seat_id); event_copy->session_id = g_strdup (event->session_id); event_copy->session_type = g_strdup (event->session_type); + event_copy->display_type = g_strdup (event->display_type); event_copy->session_x11_display = g_strdup (event->session_x11_display); event_copy->session_x11_display_device = g_strdup (event->session_x11_display_device); event_copy->session_display_device = g_strdup (event->session_display_device); @@ -415,10 +421,11 @@ add_log_for_seat_session_added (GString *str, e = (CkLogSeatSessionAddedEvent *)event; g_string_append_printf (str, - "seat-id='%s' session-id='%s' session-type='%s' session-x11-display='%s' session-x11-display-device='%s' session-display-device='%s' session-remote-host-name='%s' session-is-local=%s session-unix-user=%u session-creation-time='%s'", + "seat-id='%s' session-id='%s' session-type='%s' display-type='%s' session-x11-display='%s' session-x11-display-device='%s' session-display-device='%s' session-remote-host-name='%s' session-is-local=%s session-unix-user=%u session-creation-time='%s'", e->seat_id ? e->seat_id : "", e->session_id ? e->session_id : "", e->session_type ? e->session_type : "", + e->display_type ? e->display_type : "", e->session_x11_display ? e->session_x11_display : "", e->session_x11_display_device ? e->session_x11_display_device : "", e->session_display_device ? e->session_display_device : "", @@ -436,10 +443,11 @@ add_log_for_seat_session_removed (GString *str, e = (CkLogSeatSessionRemovedEvent *)event; g_string_append_printf (str, - "seat-id='%s' session-id='%s' session-type='%s' session-x11-display='%s' session-x11-display-device='%s' session-display-device='%s' session-remote-host-name='%s' session-is-local=%s session-unix-user=%u session-creation-time='%s'", + "seat-id='%s' session-id='%s' session-type='%s' display-type='%s' session-x11-display='%s' session-x11-display-device='%s' session-display-device='%s' session-remote-host-name='%s' session-is-local=%s session-unix-user=%u session-creation-time='%s'", e->seat_id ? e->seat_id : "", e->session_id ? e->session_id : "", e->session_type ? e->session_type : "", + e->display_type ? e->display_type : "", e->session_x11_display ? e->session_x11_display : "", e->session_x11_display_device ? e->session_x11_display_device : "", e->session_display_device ? e->session_display_device : "", @@ -939,7 +947,7 @@ parse_log_for_seat_session_added (const GString *str, } error = NULL; - re = g_regex_new ("seat-id='(?P[a-zA-Z0-9/]+)' session-id='(?P[a-zA-Z0-9/]+)' session-type='(?P[a-zA-Z0-9 ]*)' session-x11-display='(?P[0-9a-zA-Z.:]*)' session-x11-display-device='(?P[^']*)' session-display-device='(?P[^']*)' session-remote-host-name='(?P[^']*)' session-is-local=(?P[a-zA-Z]*) session-unix-user=(?P[0-9]*) session-creation-time='(?P[^']*)'", 0, 0, &error); + re = g_regex_new ("seat-id='(?P[a-zA-Z0-9/]+)' session-id='(?P[a-zA-Z0-9/]+)' session-type='(?P[a-zA-Z0-9 ]*)' display-type='(?P[a-zA-Z0-9 ]*)' session-x11-display='(?P[0-9a-zA-Z.:]*)' session-x11-display-device='(?P[^']*)' session-display-device='(?P[^']*)' session-remote-host-name='(?P[^']*)' session-is-local=(?P[a-zA-Z]*) session-unix-user=(?P[0-9]*) session-creation-time='(?P[^']*)'", 0, 0, &error); if (re == NULL) { g_warning ("%s", error->message); goto out; @@ -957,6 +965,7 @@ parse_log_for_seat_session_added (const GString *str, e->seat_id = g_match_info_fetch_named (match_info, "seatid"); e->session_id = g_match_info_fetch_named (match_info, "sessionid"); e->session_type = g_match_info_fetch_named (match_info, "sessiontype"); + e->display_type = g_match_info_fetch_named (match_info, "displaytype"); e->session_x11_display = g_match_info_fetch_named (match_info, "sessionx11display"); e->session_x11_display_device = g_match_info_fetch_named (match_info, "sessionx11displaydevice"); e->session_display_device = g_match_info_fetch_named (match_info, "sessiondisplaydevice"); @@ -1014,7 +1023,7 @@ parse_log_for_seat_session_removed (const GString *str, } error = NULL; - re = g_regex_new ("seat-id='(?P[a-zA-Z0-9/]+)' session-id='(?P[a-zA-Z0-9/]+)' session-type='(?P[a-zA-Z0-9 ]*)' session-x11-display='(?P[0-9a-zA-Z.:]*)' session-x11-display-device='(?P[^']*)' session-display-device='(?P[^']*)' session-remote-host-name='(?P[^']*)' session-is-local=(?P[a-zA-Z]*) session-unix-user=(?P[0-9]*) session-creation-time='(?P[^']*)'", 0, 0, &error); + re = g_regex_new ("seat-id='(?P[a-zA-Z0-9/]+)' session-id='(?P[a-zA-Z0-9/]+)' session-type='(?P[a-zA-Z0-9 ]*)' display-type='(?P[a-zA-Z0-9 ]*)' session-x11-display='(?P[0-9a-zA-Z.:]*)' session-x11-display-device='(?P[^']*)' session-display-device='(?P[^']*)' session-remote-host-name='(?P[^']*)' session-is-local=(?P[a-zA-Z]*) session-unix-user=(?P[0-9]*) session-creation-time='(?P[^']*)'", 0, 0, &error); if (re == NULL) { g_warning ("%s", error->message); goto out; @@ -1032,6 +1041,7 @@ parse_log_for_seat_session_removed (const GString *str, e->seat_id = g_match_info_fetch_named (match_info, "seatid"); e->session_id = g_match_info_fetch_named (match_info, "sessionid"); e->session_type = g_match_info_fetch_named (match_info, "sessiontype"); + e->display_type = g_match_info_fetch_named (match_info, "displaytype"); e->session_x11_display = g_match_info_fetch_named (match_info, "sessionx11display"); e->session_x11_display_device = g_match_info_fetch_named (match_info, "sessionx11displaydevice"); e->session_display_device = g_match_info_fetch_named (match_info, "sessiondisplaydevice"); diff --git a/src/ck-log-event.h b/src/ck-log-event.h index 65571f0..2d4ed4e 100644 --- a/src/ck-log-event.h +++ b/src/ck-log-event.h @@ -68,6 +68,7 @@ typedef struct { char *seat_id; int seat_kind; + char *seat_type; } CkLogSeatAddedEvent; typedef struct @@ -81,11 +82,13 @@ typedef struct char *seat_id; char *session_id; char *session_type; + char *display_type; char *session_x11_display; char *session_x11_display_device; char *session_display_device; char *session_remote_host_name; gboolean session_is_local; + gboolean session_is_dynamic; guint session_unix_user; char *session_creation_time; } CkLogSeatSessionAddedEvent; @@ -95,11 +98,13 @@ typedef struct char *seat_id; char *session_id; char *session_type; + char *display_type; char *session_x11_display; char *session_x11_display_device; char *session_display_device; char *session_remote_host_name; gboolean session_is_local; + gboolean session_is_dynamic; guint session_unix_user; char *session_creation_time; } CkLogSeatSessionRemovedEvent; diff --git a/src/ck-manager.c b/src/ck-manager.c index a040be3..ec4fce3 100644 --- a/src/ck-manager.c +++ b/src/ck-manager.c @@ -45,9 +45,14 @@ #include #endif +#ifndef HAVE_STRVERSCMP +#include "strverscmp.h" +#endif + #include "ck-manager.h" #include "ck-manager-glue.h" #include "ck-seat.h" +#include "ck-display-template.h" #include "ck-session-leader.h" #include "ck-session.h" #include "ck-marshal.h" @@ -57,12 +62,19 @@ #define CK_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_MANAGER, CkManagerPrivate)) +#define CK_TYPE_PARAMETER_STRUCT (dbus_g_type_get_struct ("GValueArray", \ + G_TYPE_STRING, \ + G_TYPE_VALUE, \ + G_TYPE_INVALID)) + #define CK_SEAT_DIR SYSCONFDIR "/ConsoleKit/seats.d" #define LOG_FILE LOCALSTATEDIR "/log/ConsoleKit/history" #define CK_DBUS_PATH "/org/freedesktop/ConsoleKit" #define CK_MANAGER_DBUS_PATH CK_DBUS_PATH "/Manager" #define CK_MANAGER_DBUS_NAME "org.freedesktop.ConsoleKit.Manager" +#define IS_STR_SET(x) (x != NULL && x[0] != '\0') + struct CkManagerPrivate { #ifdef HAVE_POLKIT @@ -391,6 +403,7 @@ log_seat_added_event (CkManager *manager, GError *error; char *sid; CkSeatKind seat_kind; + char *seat_type; memset (&event, 0, sizeof (CkLogEvent)); @@ -400,9 +413,11 @@ log_seat_added_event (CkManager *manager, sid = NULL; ck_seat_get_id (seat, &sid, NULL); ck_seat_get_kind (seat, &seat_kind, NULL); + ck_seat_get_type_string (seat, &seat_type, NULL); event.event.seat_added.seat_id = (char *)get_object_id_basename (sid); event.event.seat_added.seat_kind = (int)seat_kind; + event.event.seat_added.seat_type = (char *)seat_type; error = NULL; res = ck_event_logger_queue_event (manager->priv->logger, &event, &error); @@ -412,6 +427,7 @@ log_seat_added_event (CkManager *manager, } g_free (sid); + g_free (seat_type); } static void @@ -516,6 +532,7 @@ log_seat_session_added_event (CkManager *manager, if (session != NULL) { g_object_get (session, "session-type", &event.event.seat_session_added.session_type, + "display-type", &event.event.seat_session_added.display_type, "x11-display", &event.event.seat_session_added.session_x11_display, "x11-display-device", &event.event.seat_session_added.session_x11_display_device, "display-device", &event.event.seat_session_added.session_display_device, @@ -571,6 +588,7 @@ log_seat_session_removed_event (CkManager *manager, if (session != NULL) { g_object_get (session, "session-type", &event.event.seat_session_removed.session_type, + "display-type", &event.event.seat_session_removed.display_type, "x11-display", &event.event.seat_session_removed.session_x11_display, "x11-display-device", &event.event.seat_session_removed.session_x11_display_device, "display-device", &event.event.seat_session_removed.session_display_device, @@ -844,6 +862,8 @@ ready_cb (PolkitAuthority *authority, PolkitAuthorizationResult *ret; GError *error; + g_debug ("CkManager: Ready."); + error = NULL; ret = polkit_authority_check_authorization_finish (authority, res, &error); if (error != NULL) { @@ -974,6 +994,7 @@ session_is_real_user (CkSession *session, /* filter out GDM user */ if (username != NULL && strcmp (username, "gdm") == 0) { + g_debug ("CkManager: Session is not real user"); ret = FALSE; goto out; } @@ -982,6 +1003,7 @@ session_is_real_user (CkSession *session, *userp = g_strdup (username); } + g_debug ("CkManager: Session is real user"); ret = TRUE; out: @@ -1242,6 +1264,7 @@ on_seat_active_session_changed_full (CkSeat *seat, { char *ssid = NULL; + g_debug ("CkManager: Active session changed."); if (session != NULL) { ck_session_get_id (session, &ssid, NULL); } @@ -1261,6 +1284,7 @@ on_seat_session_added_full (CkSeat *seat, { char *ssid = NULL; + g_debug ("CkManager: Session added."); ck_session_get_id (session, &ssid, NULL); ck_manager_dump (manager); @@ -1278,6 +1302,7 @@ on_seat_session_removed_full (CkSeat *seat, { char *ssid = NULL; + g_debug ("CkManager: Seat Session removed."); ck_session_get_id (session, &ssid, NULL); ck_manager_dump (manager); @@ -1293,6 +1318,7 @@ on_seat_device_added (CkSeat *seat, GValueArray *device, CkManager *manager) { + g_debug ("CkManager: Seat device added."); ck_manager_dump (manager); log_seat_device_added_event (manager, seat, device); } @@ -1302,6 +1328,7 @@ on_seat_device_removed (CkSeat *seat, GValueArray *device, CkManager *manager) { + g_debug ("CkManager: Seat device removed."); ck_manager_dump (manager); log_seat_device_removed_event (manager, seat, device); } @@ -1329,15 +1356,23 @@ disconnect_seat_signals (CkManager *manager, } static CkSeat * -add_new_seat (CkManager *manager, - CkSeatKind kind) +add_new_seat (CkManager *manager, + const char *give_sid, + CkSeatKind kind, + const char *type) { char *sid; CkSeat *seat; - sid = generate_seat_id (manager); + if (IS_STR_SET (give_sid)) { + sid = g_strdup (give_sid); + } else { + sid = generate_seat_id (manager); + } + + g_debug ("CkManager: Add new seat '%s'.", sid); - seat = ck_seat_new (sid, kind); + seat = ck_seat_new (sid, kind, type); /* First we connect our own signals to the seat, followed by * the D-Bus signal hookup to make sure we can first dump the @@ -1362,7 +1397,7 @@ add_new_seat (CkManager *manager, ck_seat_run_programs (seat, NULL, NULL, "seat_added"); g_debug ("Emitting seat-added: %s", sid); - g_signal_emit (manager, signals [SEAT_ADDED], 0, sid); + g_signal_emit (manager, signals [SEAT_ADDED], 0, sid, type); log_seat_added_event (manager, seat); @@ -1381,6 +1416,8 @@ remove_seat (CkManager *manager, sid = NULL; ck_seat_get_id (seat, &sid, NULL); + g_debug ("CkManager: Remove seat '%s'", sid); + /* Need to get the original key/value */ res = g_hash_table_lookup_extended (manager->priv->seats, sid, @@ -1420,64 +1457,22 @@ remove_seat (CkManager *manager, g_free (sid); } -#define IS_STR_SET(x) (x != NULL && x[0] != '\0') - static CkSeat * find_seat_for_session (CkManager *manager, CkSession *session) { CkSeat *seat; - gboolean is_static_x11; - gboolean is_static_text; - char *display_device; - char *x11_display_device; - char *x11_display; - char *remote_host_name; - gboolean is_local; - - is_static_text = FALSE; - is_static_x11 = FALSE; - - seat = NULL; - display_device = NULL; - x11_display_device = NULL; - x11_display = NULL; - remote_host_name = NULL; - is_local = FALSE; - - /* FIXME: use matching to group entries? */ - - ck_session_get_display_device (session, &display_device, NULL); - ck_session_get_x11_display_device (session, &x11_display_device, NULL); - ck_session_get_x11_display (session, &x11_display, NULL); - ck_session_get_remote_host_name (session, &remote_host_name, NULL); - ck_session_is_local (session, &is_local, NULL); - - if (IS_STR_SET (x11_display) - && IS_STR_SET (x11_display_device) - && ! IS_STR_SET (remote_host_name) - && is_local == TRUE) { - is_static_x11 = TRUE; - } else if (! IS_STR_SET (x11_display) - && ! IS_STR_SET (x11_display_device) - && IS_STR_SET (display_device) - && ! IS_STR_SET (remote_host_name) - && is_local == TRUE) { - is_static_text = TRUE; - } - - if (is_static_x11 || is_static_text) { - char *sid; + char *sid = NULL; + + ck_session_get_seat_id (session, &sid, NULL); + + if (! IS_STR_SET (sid)) { sid = g_strdup_printf ("%s/Seat%u", CK_DBUS_PATH, 1); - seat = g_hash_table_lookup (manager->priv->seats, sid); - g_free (sid); } - g_free (display_device); - g_free (x11_display_device); - g_free (x11_display); - g_free (remote_host_name); + seat = g_hash_table_lookup (manager->priv->seats, sid); + g_free (sid); return seat; } @@ -1612,36 +1607,52 @@ open_session_for_leader (CkManager *manager, CkSession *session; CkSeat *seat; const char *ssid; + char *sid; const char *cookie; + g_debug ("CkManager: Open session for leader."); ssid = ck_session_leader_peek_session_id (leader); cookie = ck_session_leader_peek_cookie (leader); - session = ck_session_new_with_parameters (ssid, - cookie, - parameters); + session = g_hash_table_lookup (manager->priv->sessions, ssid); if (session == NULL) { - GError *error; - g_debug ("Unable to create new session"); - error = g_error_new (CK_MANAGER_ERROR, - CK_MANAGER_ERROR_GENERAL, - "Unable to create new session"); - dbus_g_method_return_error (context, error); - g_error_free (error); + g_debug ("CkManager: Creating new session."); + session = ck_session_new_with_parameters (ssid, + parameters); + + if (session == NULL) { + GError *error; + g_debug ("CkManager: Unable to create new session"); + error = g_error_new (CK_MANAGER_ERROR, + CK_MANAGER_ERROR_GENERAL, + "Unable to create new session"); + dbus_g_method_return_error (context, error); + g_error_free (error); - return; + return; + } + + g_hash_table_insert (manager->priv->sessions, + g_strdup (ssid), + g_object_ref (session)); + + } else { + g_debug ("CkManager: Using found session."); + ck_session_set_parameters (session, parameters); } - g_hash_table_insert (manager->priv->sessions, - g_strdup (ssid), - g_object_ref (session)); + ck_session_set_cookie (session, cookie, NULL); + ck_session_set_is_open (session, TRUE, NULL); /* Add to seat */ seat = find_seat_for_session (manager, session); if (seat == NULL) { + sid = NULL; + ck_session_get_seat_id (session, &sid, NULL); /* create a new seat */ - seat = add_new_seat (manager, CK_SEAT_KIND_DYNAMIC); + seat = add_new_seat (manager, sid, CK_SEAT_KIND_DYNAMIC, "Default"); + g_free (sid); } ck_seat_add_session (seat, session, NULL); @@ -1863,6 +1874,7 @@ generate_session_for_leader (CkManager *manager, { gboolean res; + g_debug ("CkManager: Generate session for leader."); res = ck_session_leader_collect_parameters (leader, context, (CkSessionLeaderDoneFunc)collect_parameters_cb, @@ -1877,11 +1889,57 @@ generate_session_for_leader (CkManager *manager, } } +static char * +check_parameters_for_ssid (const GPtrArray *parameters) +{ + int i; + + if (parameters == NULL) { + return NULL; + } + + for (i = 0; i < parameters->len; i++) { + GValue val_struct = { 0, }; + char *prop_name; + gboolean res; + + g_value_init (&val_struct, CK_TYPE_PARAMETER_STRUCT); + g_value_set_static_boxed (&val_struct, g_ptr_array_index (parameters, i)); + + res = dbus_g_type_struct_get (&val_struct, + 0, &prop_name, + G_MAXUINT); + if (! res) { + g_debug ("CkManager: Unable to read parameter name"); + continue; + } + + if (prop_name != NULL && strcmp (prop_name, "session") == 0) { + GValue prop_val = { 0, }; + GValue *session_val; + + g_value_init (&prop_val, G_TYPE_VALUE); + res = dbus_g_type_struct_get_member (&val_struct, 1, &prop_val); + + if (! res) { + g_debug ("CkManager: Unable to read parameter value"); + continue; + } + + session_val = g_value_get_boxed (&prop_val); + + return g_value_dup_string (session_val); + } + } + + return NULL; +} + static gboolean -create_session_for_sender (CkManager *manager, - const char *sender, - const GPtrArray *parameters, - DBusGMethodInvocation *context) +open_session_for_sender (CkManager *manager, + const char *sender, + const GPtrArray *parameters, + DBusGMethodInvocation *context) { pid_t pid; uid_t uid; @@ -1889,6 +1947,7 @@ create_session_for_sender (CkManager *manager, char *cookie; char *ssid; CkSessionLeader *leader; + CkSession *session; g_debug ("CkManager: create session for sender: %s", sender); @@ -1907,9 +1966,21 @@ create_session_for_sender (CkManager *manager, } cookie = generate_session_cookie (manager); - ssid = generate_session_id (manager); - g_debug ("Creating new session ssid: %s", ssid); + ssid = check_parameters_for_ssid (parameters); + + if (IS_STR_SET (ssid)) { + session = g_hash_table_lookup (manager->priv->sessions, ssid); + + /* FIXME: Need to verify that the session belongs to a seat + * managed by the sender + */ + g_debug ("CkManager: Managing existing session ssid: %s", ssid); + } else { + ssid = generate_session_id (manager); + session = NULL; + g_debug ("CkManager: Creating new session ssid: %s", ssid); + } leader = ck_session_leader_new (); ck_session_leader_set_uid (leader, uid); @@ -2148,7 +2219,7 @@ ck_manager_open_session (CkManager *manager, gboolean ret; sender = dbus_g_method_get_sender (context); - ret = create_session_for_sender (manager, sender, NULL, context); + ret = open_session_for_sender (manager, sender, NULL, context); g_free (sender); return ret; @@ -2163,7 +2234,7 @@ ck_manager_open_session_with_parameters (CkManager *manager, gboolean ret; sender = dbus_g_method_get_sender (context); - ret = create_session_for_sender (manager, sender, parameters, context); + ret = open_session_for_sender (manager, sender, parameters, context); g_free (sender); return ret; @@ -2180,10 +2251,12 @@ remove_session_for_cookie (CkManager *manager, char *sid; gboolean res; gboolean ret; + gboolean should_remove_session; ret = FALSE; orig_ssid = NULL; orig_session = NULL; + should_remove_session = FALSE; g_debug ("Removing session for cookie: %s", cookie); @@ -2210,6 +2283,17 @@ remove_session_for_cookie (CkManager *manager, goto out; } + ck_session_set_is_open (orig_session, FALSE, NULL); + ck_session_set_cookie (orig_session, NULL, NULL); + ck_session_set_active (orig_session, FALSE, NULL); + ck_session_set_unix_user (orig_session, 0, NULL); + ck_session_set_x11_display (orig_session, NULL, NULL); + ck_session_set_x11_display_device (orig_session, NULL, NULL); + ck_session_set_display_device (orig_session, NULL, NULL); + ck_session_set_login_session_id (orig_session, NULL, NULL); + ck_session_set_remote_host_name (orig_session, NULL, NULL); + ck_session_set_under_request (orig_session, FALSE, NULL); + /* Must keep a reference to the session in the manager until * all events for seats are cleared. So don't remove * or steal the session from the master list until @@ -2217,31 +2301,33 @@ remove_session_for_cookie (CkManager *manager, * for seat removals doesn't work. */ - /* remove from seat */ - sid = NULL; - ck_session_get_seat_id (orig_session, &sid, NULL); - if (sid != NULL) { - CkSeat *seat; - seat = g_hash_table_lookup (manager->priv->seats, sid); - if (seat != NULL) { - CkSeatKind kind; - - ck_seat_remove_session (seat, orig_session, NULL); - - kind = CK_SEAT_KIND_STATIC; - /* if dynamic seat has no sessions then remove it */ - ck_seat_get_kind (seat, &kind, NULL); - if (kind == CK_SEAT_KIND_DYNAMIC) { - remove_seat (manager, seat); + ck_session_get_remove_on_close (orig_session, &should_remove_session, NULL); + + if (should_remove_session) { + /* remove from seat */ + g_debug ("CkManager: Should remove session"); + sid = NULL; + ck_session_get_seat_id (orig_session, &sid, NULL); + if (sid != NULL) { + CkSeat *seat; + seat = g_hash_table_lookup (manager->priv->seats, sid); + if (seat != NULL) { + CkSeatKind kind; + + ck_seat_remove_session (seat, orig_session, NULL); + + kind = CK_SEAT_KIND_STATIC; + /* if dynamic seat has no sessions then remove it */ + ck_seat_get_kind (seat, &kind, NULL); } } - } - g_free (sid); + g_free (sid); - /* Remove the session from the list but don't call - * unref until we are done with it */ - g_hash_table_steal (manager->priv->sessions, - ck_session_leader_peek_session_id (leader)); + /* Remove the session from the list but don't call + * unref until we are done with it */ + g_hash_table_steal (manager->priv->sessions, + ck_session_leader_peek_session_id (leader)); + } ck_manager_dump (manager); @@ -2249,11 +2335,13 @@ remove_session_for_cookie (CkManager *manager, ret = TRUE; out: - if (orig_session != NULL) { - g_object_unref (orig_session); - } - g_free (orig_ssid); + if (should_remove_session) { + if (orig_session != NULL) { + g_object_unref (orig_session); + } + g_free (orig_ssid); + } return ret; } @@ -2374,6 +2462,7 @@ remove_leader_for_connection (const char *cookie, g_assert (leader != NULL); g_assert (data->service_name != NULL); + g_debug ("CkManager: Remove leader for connection"); name = ck_session_leader_peek_service_name (leader); if (strcmp (name, data->service_name) == 0) { remove_session_for_cookie (data->manager, cookie, NULL); @@ -2391,6 +2480,8 @@ remove_sessions_for_connection (CkManager *manager, guint n_removed; RemoveLeaderData data; + g_debug ("CkManager: Remove sessions for connection"); + data.service_name = service_name; data.manager = manager; @@ -2426,6 +2517,8 @@ register_manager (CkManager *manager) manager->priv->pol_ctx = polkit_authority_get (); #endif + g_debug ("CkManager: Registering manager"); + error = NULL; manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); if (manager->priv->connection == NULL) { @@ -2471,9 +2564,11 @@ ck_manager_class_init (CkManagerClass *klass) G_STRUCT_OFFSET (CkManagerClass, seat_added), NULL, NULL, - g_cclosure_marshal_VOID__BOXED, + ck_marshal_VOID__STRING_STRING, G_TYPE_NONE, - 1, DBUS_TYPE_G_OBJECT_PATH); + 2, + G_TYPE_STRING, + G_TYPE_STRING); signals [SEAT_REMOVED] = g_signal_new ("seat-removed", G_TYPE_FROM_CLASS (object_class), @@ -2579,6 +2674,43 @@ ck_manager_get_seats (CkManager *manager, return TRUE; } +static void +listify_unmanaged_seat_ids (char *id, + CkSeat *seat, + GPtrArray **array) +{ + if (ck_seat_is_managed (seat)) { + return; + } + + g_ptr_array_add (*array, g_strdup (id)); +} + + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.GetUnmanagedSeats +*/ +gboolean +ck_manager_get_unmanaged_seats (CkManager *manager, + GPtrArray **seats, + GError **error) +{ + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + if (seats == NULL) { + return FALSE; + } + + *seats = g_ptr_array_new (); + g_hash_table_foreach (manager->priv->seats, (GHFunc)listify_unmanaged_seat_ids, seats); + + return TRUE; +} + static void listify_session_ids (char *id, CkSession *session, @@ -2604,16 +2736,321 @@ ck_manager_get_sessions (CkManager *manager, return TRUE; } +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.AddSeat string:Default +*/ +gboolean +ck_manager_add_seat (CkManager *manager, + const char *type, + char **sid, + GError **error) +{ + CkSeat *seat; + + g_debug ("CkManager: Add seat '%s'.", *sid); + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + seat = add_new_seat (manager, NULL, CK_SEAT_KIND_DYNAMIC, type); + + if (!ck_seat_get_id (seat, sid, error)) { + return FALSE; + } + + return TRUE; +} + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.AddSeatById \ + objpath:/org/freedesktop/ConsoleKit/SeatTest \ +*/ +gboolean +ck_manager_add_seat_by_id (CkManager *manager, + const char *type, + const char *sid, + GError **error) +{ + CkSeat *seat; + + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + seat = add_new_seat (manager, sid, CK_SEAT_KIND_DYNAMIC, type); + + return !(seat == NULL); +} + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.RemoveSeat \ + obj:/org/freedesktop/ConsoleKit/Seat2 +*/ +gboolean +ck_manager_remove_seat (CkManager *manager, + const char *sid, + DBusGMethodInvocation *context) +{ + CkSeat *seat = NULL; + CkSeatKind kind; + + g_debug ("CkManager: Remove seat '%s'.", sid); + g_return_val_if_fail (CK_IS_MANAGER (manager), FALSE); + + seat = g_hash_table_lookup (manager->priv->seats, sid); + + if (seat == NULL) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Seat '%s' doesn't exist"), + sid); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + ck_seat_get_kind (seat, &kind, NULL); + + if (kind == CK_SEAT_KIND_STATIC) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Seat '%s' is static and can't be removed"), + sid); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + if (ck_seat_is_managed (seat)) { + ck_seat_request_removal (seat); + } else { + remove_seat (manager, seat); + } + + return TRUE; +} + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.AddSession \ + objpath:/org/freedesktop/ConsoleKit/Seat2 \ + string:"LoginWindow" \ + dict:string:string:"vt","vt9","display",":123" +*/ +gboolean +ck_manager_add_session (CkManager *manager, + const char *sid, + const char *type, + const char *display_type, + GHashTable *variables, + DBusGMethodInvocation *context) +{ + CkSeat *seat; + CkSession *session; + char *ssid; + + g_debug ("CkManager: Add session"); + seat = g_hash_table_lookup (manager->priv->seats, sid); + + if (seat == NULL) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Seat '%s' doesn't exist"), + sid); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + ssid = generate_session_id (manager); + + session = ck_session_new (ssid, type, display_type, variables); + + if (session == NULL) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Session could not be added to seat '%s'"), + sid); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + ck_session_set_seat_id (session, sid, NULL); + if (IS_STR_SET (type) && g_str_equal (type, "LoginWindow")) { + session_set_remove_on_close (session, FALSE, NULL); + } else { + session_set_remove_on_close (session, TRUE, NULL); + } + + ck_seat_add_session (seat, session, NULL); + + g_hash_table_insert (manager->priv->sessions, + ssid, + session); + + dbus_g_method_return (context, ssid); + return TRUE; +} + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.RemoveSession \ + objpath:/org/freedesktop/ConsoleKit/Session2 +*/ +gboolean +ck_manager_remove_session (CkManager *manager, + const char *ssid, + DBusGMethodInvocation *context) +{ + CkSession *session; + CkSeat *seat; + GError *error; + char *sid; + gboolean is_open; + + g_debug ("CkManager: Remove session."); + session = g_hash_table_lookup (manager->priv->sessions, ssid); + + if (session == NULL) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Session '%s' doesn't exist"), + ssid); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + ck_session_get_seat_id (session, &sid, NULL); + seat = g_hash_table_lookup (manager->priv->seats, sid); + g_free (sid); + + if (seat == NULL) { + g_warning ("Session '%s' is not associated with a seat", ssid); + g_hash_table_remove (manager->priv->sessions, ssid); + return TRUE; + } + + error = NULL; + + ck_session_is_open (session, &is_open, NULL); + session_set_remove_on_close (session, TRUE, NULL); + + /* We'll let the seat manager close us when it's ready + */ + if (ck_seat_is_managed (seat) && is_open) { + ck_seat_request_close_session (seat, session, NULL); + dbus_g_method_return (context); + + return TRUE; + } + + if (!ck_seat_remove_session (seat, session, &error)) { + if (error == NULL) { + return TRUE; + } + + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + g_hash_table_remove (manager->priv->sessions, ssid); + dbus_g_method_return (context); + return TRUE; +} + +static void +add_sessions_from_seat (CkManager *manager, + CkSeat *seat) +{ + GPtrArray *sessions; + int i; + + g_debug ("CkManager: Add session from seat."); + ck_seat_get_sessions (seat, &sessions, NULL); + + for (i = 0; i < sessions->len; i++) { + char *ssid; + CkSession *session; + + ssid = g_ptr_array_index (sessions, i); + session = ck_seat_get_session (seat, ssid); + + g_hash_table_insert (manager->priv->sessions, + ssid, + session); + } + + g_ptr_array_free (sessions, TRUE); +} + static void add_seat_for_file (CkManager *manager, const char *filename) { char *sid; + char *orig_sid; CkSeat *seat; + g_debug ("CkManager: Add seat for file."); sid = generate_seat_id (manager); + orig_sid = g_strdup (sid); + seat = ck_seat_new_from_file (&sid, filename); - seat = ck_seat_new_from_file (sid, filename); + if (seat == NULL) { + /* returns null if connection to bus fails */ + g_free (sid); + g_free (orig_sid); + manager->priv->seat_serial--; + return; + } + + if (!g_str_equal (orig_sid, sid)) { + manager->priv->seat_serial--; + } + g_free (orig_sid); + + add_sessions_from_seat (manager, seat); + + if (seat == NULL) { + return; + } if (seat == NULL) { return; @@ -2636,7 +3073,7 @@ add_seat_for_file (CkManager *manager, ck_seat_run_programs (seat, NULL, NULL, "seat_added"); g_debug ("Emitting seat-added: %s", sid); - g_signal_emit (manager, signals [SEAT_ADDED], 0, sid); + g_signal_emit (manager, signals [SEAT_ADDED], 0, sid, "Default"); log_seat_added_event (manager, seat); } @@ -2647,6 +3084,7 @@ load_seats_from_dir (CkManager *manager) GDir *d; GError *error; const char *file; + GQueue seat_queue; error = NULL; d = g_dir_open (CK_SEAT_DIR, @@ -2658,17 +3096,26 @@ load_seats_from_dir (CkManager *manager) return FALSE; } + g_queue_init (&seat_queue); while ((file = g_dir_read_name (d)) != NULL) { if (g_str_has_suffix (file, ".seat")) { char *path; path = g_build_filename (CK_SEAT_DIR, file, NULL); - add_seat_for_file (manager, path); - g_free (path); + g_queue_push_tail (&seat_queue, path); } } - g_dir_close (d); + g_queue_sort (&seat_queue, (GCompareDataFunc) strverscmp, NULL); + + while (!g_queue_is_empty (&seat_queue)) { + char *path; + + path = g_queue_pop_head (&seat_queue); + add_seat_for_file (manager, path); + g_free (path); + } + return TRUE; } @@ -2704,8 +3151,6 @@ ck_manager_init (CkManager *manager) (GDestroyNotify) g_object_unref); manager->priv->logger = ck_event_logger_new (LOG_FILE); - - create_seats (manager); } static void @@ -2750,7 +3195,48 @@ ck_manager_new (void) g_object_unref (manager_object); return NULL; } + + create_seats (CK_MANAGER (manager_object)); } return CK_MANAGER (manager_object); } + +gboolean +ck_manager_will_not_respawn (CkManager *manager, + const char *ssid, + DBusGMethodInvocation *context) +{ + GError *error; + CkSeat *seat; + CkSession *session; + char *sid; + + g_debug ("CkManager: Will not respawn: '%s'.", ssid); + session = g_hash_table_lookup (manager->priv->sessions, ssid); + + if (session == NULL) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Session '%s' doesn't exist"), + ssid); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + session_set_remove_on_close (session, FALSE, NULL); + ck_session_get_seat_id (session, &sid, NULL); + seat = g_hash_table_lookup (manager->priv->seats, sid); + g_free (sid); + + ck_seat_no_respawn (seat, session, &error); + + dbus_g_method_return (context); + return TRUE; +} + diff --git a/src/ck-manager.h b/src/ck-manager.h index 4bd56e8..d3b23b6 100644 --- a/src/ck-manager.h +++ b/src/ck-manager.h @@ -49,7 +49,8 @@ typedef struct GObjectClass parent_class; void (* seat_added) (CkManager *manager, - const char *sid); + const char *sid, + const char *type); void (* seat_removed) (CkManager *manager, const char *sid); void (* system_idle_hint_changed) (CkManager *manager, @@ -96,6 +97,9 @@ gboolean ck_manager_get_sessions (CkManager gboolean ck_manager_get_seats (CkManager *manager, GPtrArray **seats, GError **error); +gboolean ck_manager_get_unmanaged_seats (CkManager *manager, + GPtrArray **seats, + GError **error); gboolean ck_manager_close_session (CkManager *manager, const char *cookie, DBusGMethodInvocation *context); @@ -128,6 +132,31 @@ gboolean ck_manager_open_session_with_parameters (CkManager const GPtrArray *parameters, DBusGMethodInvocation *context); +gboolean ck_manager_add_seat (CkManager *manager, + const char *type, + char **sid, + GError **error); +gboolean ck_manager_add_seat_by_id (CkManager *manager, + const char *type, + const char *sid, + GError **error); +gboolean ck_manager_remove_seat (CkManager *manager, + const char *sid, + DBusGMethodInvocation *context); + +gboolean ck_manager_add_session (CkManager *manager, + const char *sid, + const char *type, + const char *display_type, + GHashTable *parameters, + DBusGMethodInvocation *context); +gboolean ck_manager_remove_session (CkManager *manager, + const char *ssid, + DBusGMethodInvocation *context); +gboolean ck_manager_will_not_respawn (CkManager *manager, + const char *cookie, + DBusGMethodInvocation *context); + G_END_DECLS #endif /* __CK_MANAGER_H */ diff --git a/src/ck-marshal.list b/src/ck-marshal.list index 7f60efc..74a63e5 100644 --- a/src/ck-marshal.list +++ b/src/ck-marshal.list @@ -1,3 +1,6 @@ VOID:UINT,STRING BOOLEAN:POINTER VOID:OBJECT,OBJECT +VOID:STRING,STRING +VOID:STRING,BOOLEAN,STRING,POINTER,STRING,POINTER +VOID:STRING,STRING,STRING,POINTER,STRING,POINTER diff --git a/src/ck-seat.c b/src/ck-seat.c index 59db29f..fe88177 100644 --- a/src/ck-seat.c +++ b/src/ck-seat.c @@ -39,21 +39,30 @@ #include "ck-seat-glue.h" #include "ck-marshal.h" +#include "ck-display-template.h" #include "ck-session.h" #include "ck-vt-monitor.h" #include "ck-run-programs.h" #define CK_SEAT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SEAT, CkSeatPrivate)) +#define CK_SESSION_DIR SYSCONFDIR "/ConsoleKit/sessions.d" + #define CK_DBUS_PATH "/org/freedesktop/ConsoleKit" #define CK_DBUS_NAME "org.freedesktop.ConsoleKit" #define NONULL_STRING(x) ((x) != NULL ? (x) : "") +#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) + +#define IS_STR_SET(x) (x != NULL && x[0] != '\0') + +#define CK_DBUS_TYPE_G_STRING_STRING_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)) struct CkSeatPrivate { char *id; CkSeatKind kind; + char *type; GHashTable *sessions; GPtrArray *devices; @@ -62,6 +71,8 @@ struct CkSeatPrivate CkVtMonitor *vt_monitor; DBusGConnection *connection; + + DBusGProxy *manager_proxy; }; enum { @@ -73,6 +84,10 @@ enum { SESSION_REMOVED_FULL, DEVICE_ADDED, DEVICE_REMOVED, + REMOVE_REQUEST, + OPEN_SESSION_REQUEST, + CLOSE_SESSION_REQUEST, + NO_RESPAWN, LAST_SIGNAL }; @@ -80,6 +95,7 @@ enum { PROP_0, PROP_ID, PROP_KIND, + PROP_TYPE }; static guint signals [LAST_SIGNAL] = { 0, }; @@ -286,6 +302,7 @@ ck_seat_activate_session (CkSeat *seat, { CkSession *session; gboolean ret; + gboolean is_open; g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); @@ -297,11 +314,397 @@ ck_seat_activate_session (CkSeat *seat, session = g_hash_table_lookup (seat->priv->sessions, ssid); } - ret = _seat_activate_session (seat, session, context); + ck_session_is_open (session, &is_open, NULL); + if (!is_open) { + ret = ck_seat_request_open_session (seat, session, NULL); + dbus_g_method_return (context, NULL); + } else { + ret = _seat_activate_session (seat, session, context); + } return ret; } +static gboolean +on_substitution_match (const GMatchInfo *match_info, + GString *result, + GHashTable *substitution_variables) +{ + char *match; + char *value = NULL; + + match = g_match_info_fetch (match_info, 1); + + if (substitution_variables != NULL) + value = g_hash_table_lookup (substitution_variables, match); + + if (value != NULL) { + g_string_append (result, value); + } else { + char *original_string; + + original_string = g_match_info_fetch (match_info, 0); + g_string_append (result, original_string); + g_free (original_string); + } + g_free (match); + + return FALSE; +} + +static char * +apply_substitutions (const char *value, + GHashTable *substitution_variables) +{ + GRegex *expression; + char *expanded_string; + + expression = g_regex_new ("\\$([^[:space:]]+)", 0, 0, NULL); + expanded_string = g_regex_replace_eval (expression, + value, + -1, 0, 0, + (GRegexEvalCallback) + on_substitution_match, + substitution_variables, + NULL); + + if (expanded_string == NULL) { + expanded_string = g_strdup (value); + } + + return expanded_string; +} + +static GHashTable * +get_evaluated_parameter_map (GHashTable *parameters, + GHashTable *substitution_variables) +{ + GHashTable *evaluated_parameters; + GHashTableIter iter; + gpointer key, value; + + evaluated_parameters = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + + g_hash_table_iter_init (&iter, parameters); + while (g_hash_table_iter_next (&iter, &key, &value)) { + char *expanded_string; + + expanded_string = apply_substitutions ((char *) value, + substitution_variables); + + g_hash_table_insert (evaluated_parameters, + g_strdup ((char *) key), + expanded_string); + } + + return evaluated_parameters; +} + +static void +request_session (gpointer key, + gpointer value, + gpointer user_data) +{ + CkSeat *seat = CK_SEAT (user_data); + CkSession *session = (CkSession *) value; + + g_debug ("CkSeat: Request session"); + ck_session_set_ever_open (session, FALSE, NULL); + ck_session_set_under_request (session, FALSE, NULL); + ck_seat_request_open_session (seat, session, NULL); +} + +static void +append_hash_table_to_dbus_message_iter (DBusMessageIter *iter, + GHashTable *hash_table) +{ + GHashTableIter hash_table_iter; + gpointer key, value; + DBusMessageIter array_iter; + + dbus_message_iter_open_container (iter, + DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + + + g_hash_table_iter_init (&hash_table_iter, hash_table); + while (g_hash_table_iter_next (&hash_table_iter, &key, &value)) { + DBusMessageIter dict_iter; + + dbus_message_iter_open_container (&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, + &dict_iter); + + dbus_message_iter_append_basic (&dict_iter, DBUS_TYPE_STRING, &key); + dbus_message_iter_append_basic (&dict_iter, DBUS_TYPE_STRING, &value); + dbus_message_iter_close_container (&array_iter, &dict_iter); + } + dbus_message_iter_close_container (iter, &array_iter); +} + +static void +emit_session_open_request (CkSeat *seat, + const char *ssid, + const char *session_type, + const char *display_template_name, + GHashTable *display_variables, + const char *display_type, + GHashTable *evaluated_parameters) +{ + DBusMessage *message; + DBusConnection *connection; + DBusMessageIter iter; + + if (!ck_seat_is_managed (seat)) + return; + + message = dbus_message_new_signal (seat->priv->id, + "org.freedesktop.ConsoleKit.Seat", + "OpenSessionRequest"); + + dbus_message_set_destination (message, + dbus_g_proxy_get_bus_name (seat->priv->manager_proxy)); + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_OBJECT_PATH, &ssid); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session_type); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &display_template_name); + append_hash_table_to_dbus_message_iter (&iter, display_variables); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &display_type); + append_hash_table_to_dbus_message_iter (&iter, evaluated_parameters); + + connection = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); + dbus_connection_send (connection, message, NULL); + dbus_connection_unref (connection); + dbus_message_unref (message); +} + +gboolean +ck_seat_request_open_session (CkSeat *seat, + CkSession *session, + GError **error) +{ + char *ssid; + char *type; + CkDisplayTemplate *display_template; + GHashTable *display_variables; + GHashTable *display_parameters; + GHashTable *evaluated_parameters; + gboolean is_open; + gboolean ever_open; + gboolean under_request; + + ck_session_is_open (session, &is_open, NULL); + + if (is_open) { + return TRUE; + } + + ck_session_get_under_request (session, &under_request, NULL); + if (under_request) { + return TRUE; + } + + ck_session_set_under_request (session, TRUE, NULL); + + ck_session_get_ever_open (session, &ever_open, NULL); + + display_template = ck_session_get_display_template (session); + + if (display_template == NULL) { + return TRUE; + } + + ck_session_get_session_type (session, &type, NULL); + + if (type == NULL) { + g_object_unref (display_template); + return TRUE; + } + + /* substitute $display $vt etc */ + display_variables = ck_session_get_display_variables (session); + display_parameters = ck_display_template_get_parameters (display_template); + + if (display_parameters == NULL) { + g_free (type); + g_object_unref (display_template); + g_hash_table_unref (display_variables); + return TRUE; + } + + if (!ever_open) { + evaluated_parameters = get_evaluated_parameter_map (display_parameters, display_variables); + } else { + evaluated_parameters = get_evaluated_parameter_map (display_parameters, NULL); + } + + g_hash_table_unref (display_parameters); + + ck_session_get_id (session, &ssid, NULL); + + emit_session_open_request (seat, ssid, type, + ck_display_template_get_name (display_template), + display_variables, + ck_display_template_get_type_string (display_template), + evaluated_parameters); + + g_free (ssid); + g_free (type); + g_hash_table_unref (evaluated_parameters); + g_hash_table_unref (display_variables); + g_object_unref (display_template); + + return TRUE; +} + +static void +on_seat_manager_disappeared (CkSeat *seat) +{ + g_debug ("CkSeat: Seat Manager Disappeared."); + + g_signal_handlers_disconnect_by_func (seat->priv->manager_proxy, + G_CALLBACK (on_seat_manager_disappeared), + seat); + g_object_unref (seat->priv->manager_proxy); + seat->priv->manager_proxy = NULL; + + /* FIXME: should probably emit a signal so a new display manager + * knows that the seat is now unmanaged + * + * (maybe only if its kind is static?) + */ +} + +gboolean +ck_seat_manage (CkSeat *seat, + DBusGMethodInvocation *context) +{ + char *sender_name; + + g_debug ("CkSeat: Seat manage."); + sender_name = dbus_g_method_get_sender (context); + + if (seat->priv->manager_proxy != NULL) { + GError *error; + const char *existing_manager_name; + + existing_manager_name = dbus_g_proxy_get_bus_name (seat->priv->manager_proxy); + + if (existing_manager_name == NULL) { + g_warning ("Seat manager lacks bus unique name"); + existing_manager_name = ""; + } + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Seat already managed (by '%s')"), + existing_manager_name); + + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + /* FIXME: We pass in a bogus object path (the path we use on this side of + * the pipe) and interface here. + * + * We only use the proxy to watch for when the other side disappears, not + * for communicating with it. All communication is one-way using signals. + */ + seat->priv->manager_proxy = dbus_g_proxy_new_for_name (seat->priv->connection, + + sender_name, + seat->priv->id, + "org.freedesktop.ConsoleKit.SeatManager"); + g_free (sender_name); + + g_signal_connect_swapped (seat->priv->manager_proxy, + "destroy", + G_CALLBACK (on_seat_manager_disappeared), + seat); + + g_hash_table_foreach (seat->priv->sessions, request_session, seat); + + dbus_g_method_return (context); + return TRUE; +} + +gboolean +ck_seat_unmanage (CkSeat *seat, + DBusGMethodInvocation *context) +{ + GError *error; + const char *existing_manager_name; + char *sender_name; + + g_debug ("CkSeat: Seat unmanage."); + if (seat->priv->manager_proxy == NULL) { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Seat not managed")); + + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + sender_name = dbus_g_method_get_sender (context); + existing_manager_name = dbus_g_proxy_get_bus_name (seat->priv->manager_proxy); + + if (strcmp (sender_name, existing_manager_name) != 0) { + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Seat managed by '%s' not '%s'"), + existing_manager_name, + sender_name); + + dbus_g_method_return_error (context, error); + g_error_free (error); + + return FALSE; + } + + on_seat_manager_disappeared (seat); + + dbus_g_method_return (context); + return TRUE; +} + +void +ck_seat_request_removal (CkSeat *seat) +{ + DBusMessage *message; + DBusConnection *connection; + + g_return_if_fail (CK_IS_SEAT (seat)); + g_return_if_fail (ck_seat_is_managed (seat)); + + g_debug ("CkSeat: Seat request removal."); + + message = dbus_message_new_signal (seat->priv->id, + "org.freedesktop.ConsoleKit.Seat", + "RemoveRequest"); + + dbus_message_set_destination (message, + dbus_g_proxy_get_bus_name (seat->priv->manager_proxy)); + + connection = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); + dbus_connection_send (connection, message, NULL); + dbus_connection_unref (connection); + dbus_message_unref (message); +} + static gboolean match_session_display_device (const char *key, CkSession *session, @@ -313,6 +716,8 @@ match_session_display_device (const char *key, device = NULL; ret = FALSE; + g_debug ("CkSeat: Session display device."); + if (session == NULL) { goto out; } @@ -485,6 +890,8 @@ change_active_session (CkSeat *seat, char *ssid; CkSession *old_session; + g_debug ("CkSeat: Change active session."); + if (seat->priv->active_session == session) { return; } @@ -514,7 +921,10 @@ change_active_session (CkSeat *seat, * must be sent when the database dump is finished it is * important that the '-full' signalled is emitted first. */ - g_signal_emit (seat, signals [ACTIVE_SESSION_CHANGED_FULL], 0, old_session, session); + if (CK_IS_SESSION (old_session)) { + g_signal_emit (seat, signals [ACTIVE_SESSION_CHANGED_FULL], 0, old_session, session); + } + g_signal_emit (seat, signals [ACTIVE_SESSION_CHANGED], 0, ssid); if (old_session != NULL) { @@ -524,6 +934,42 @@ change_active_session (CkSeat *seat, g_free (ssid); } +static void +find_possible_session_to_activate (CkSeat *seat) +{ + GHashTableIter iter; + gpointer key, value; + gboolean is_open; + char *session_type = NULL; + CkSession *login_session = NULL; + + g_debug ("CkSeat: Find possible session to activate"); + g_hash_table_iter_init (&iter, seat->priv->sessions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + + ck_session_is_open (value, &is_open, NULL); + + if (is_open) { + login_session = NULL; + change_active_session (seat, value); + break; + } + + ck_session_get_session_type (value, &session_type, NULL); + if (IS_STR_SET (session_type) && + g_str_equal (session_type, "LoginWindow")) { + login_session = value; + g_free (session_type); + } + } + + if (login_session != NULL) { + ck_session_set_ever_open (login_session, FALSE, NULL); + ck_seat_request_open_session (seat, login_session, NULL); + } + +} + static void update_active_vt (CkSeat *seat, guint num) @@ -536,7 +982,12 @@ update_active_vt (CkSeat *seat, g_debug ("Active device: %s", device); session = find_session_for_display_device (seat, device); - change_active_session (seat, session); + + if (session == NULL) { + find_possible_session_to_activate (seat); + } else { + change_active_session (seat, session); + } g_free (device); } @@ -546,12 +997,20 @@ maybe_update_active_session (CkSeat *seat) { guint num; - if (seat->priv->kind != CK_SEAT_KIND_STATIC) { - return; - } + g_debug ("CkSeat: Check to see if we should update active session"); - if (ck_vt_monitor_get_active (seat->priv->vt_monitor, &num, NULL)) { - update_active_vt (seat, num); + switch (seat->priv->kind){ + case CK_SEAT_KIND_STATIC: + if (ck_vt_monitor_get_active (seat->priv->vt_monitor, + &num, NULL)) { + update_active_vt (seat, num); + } + break; + case CK_SEAT_KIND_DYNAMIC: + find_possible_session_to_activate (seat); + break; + default: + break; } } @@ -560,6 +1019,7 @@ session_activate (CkSession *session, DBusGMethodInvocation *context, CkSeat *seat) { + g_debug ("CkSeat: Session activate."); _seat_activate_session (seat, session, context); /* always return TRUE to indicate that the signal was handled */ @@ -583,6 +1043,8 @@ ck_seat_remove_session (CkSeat *seat, ssid = NULL; ck_session_get_id (session, &ssid, NULL); + g_debug ("CkSeat: Removing session '%s'", ssid); + /* Need to get the original key/value */ res = g_hash_table_lookup_extended (seat->priv->sessions, ssid, @@ -627,18 +1089,118 @@ ck_seat_remove_session (CkSeat *seat, return ret; } +static void +emit_session_close_request (CkSeat *seat, + const char *ssid) +{ + DBusMessage *message; + DBusConnection *connection; + DBusMessageIter iter; + + g_debug ("CkSeat: Emit Session Close request"); + + message = dbus_message_new_signal (seat->priv->id, + "org.freedesktop.ConsoleKit.Seat", + "CloseSessionRequest"); + + dbus_message_set_destination (message, + dbus_g_proxy_get_bus_name (seat->priv->manager_proxy)); + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_OBJECT_PATH, &ssid); + + connection = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); + dbus_connection_send (connection, message, NULL); + dbus_connection_unref (connection); + dbus_message_unref (message); +} + +gboolean +ck_seat_request_close_session (CkSeat *seat, + CkSession *session, + GError **error) +{ + char *ssid; + + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + g_return_val_if_fail (ck_seat_is_managed (seat), FALSE); + + g_debug ("CkSeat: Request close session."); + ck_session_get_id (session, &ssid, NULL); + + emit_session_close_request (seat, ssid); + + g_free (ssid); + + return FALSE; +} + +gboolean +ck_seat_no_respawn (CkSeat *seat, + CkSession *session, + GError **error) +{ + DBusMessage *message; + DBusConnection *connection; + DBusMessageIter iter; + char *ssid; + + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + g_return_val_if_fail (ck_seat_is_managed (seat), FALSE); + + g_debug ("CkSeat: No Respawn."); + ck_session_get_id (session, &ssid, NULL); + + message = dbus_message_new_signal (seat->priv->id, + "org.freedesktop.ConsoleKit.Seat", + "NoRespawn"); + + dbus_message_set_destination (message, + dbus_g_proxy_get_bus_name (seat->priv->manager_proxy)); + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_OBJECT_PATH, &ssid); + + connection = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); + dbus_connection_send (connection, message, NULL); + dbus_connection_unref (connection); + dbus_message_unref (message); + + g_free (ssid); + + return FALSE; +} + gboolean ck_seat_add_session (CkSeat *seat, CkSession *session, GError **error) { char *ssid; + GHashTableIter iter; + gpointer key, value; + gboolean found; g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + g_debug ("CkSeat: Add session."); ck_session_get_id (session, &ssid, NULL); - g_hash_table_insert (seat->priv->sessions, g_strdup (ssid), g_object_ref (session)); + found = FALSE; + g_hash_table_iter_init (&iter, seat->priv->sessions); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (g_str_equal ((gchar *)key, ssid)) { + found = TRUE; + break; + } + } + + if (! found) + g_hash_table_insert (seat->priv->sessions, g_strdup (ssid), g_object_ref (session)); + else + g_object_ref (session); ck_session_set_seat_id (session, seat->priv->id, NULL); @@ -655,6 +1217,10 @@ ck_seat_add_session (CkSeat *seat, maybe_update_active_session (seat); + if (ck_seat_is_managed (seat)) { + ck_seat_request_open_session (seat, session, NULL); + } + g_free (ssid); return TRUE; @@ -740,6 +1306,20 @@ ck_seat_get_kind (CkSeat *seat, return TRUE; } +gboolean +ck_seat_get_type_string (CkSeat *seat, + char **type, + GError **error) +{ + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + if (type != NULL) { + *type = g_strdup (seat->priv->type); + } + + return TRUE; +} + gboolean ck_seat_get_id (CkSeat *seat, char **id, @@ -772,6 +1352,7 @@ ck_seat_register (CkSeat *seat) g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); error = NULL; + g_debug ("CkSeat: Register seat."); seat->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); if (seat->priv->connection == NULL) { if (error != NULL) { @@ -858,6 +1439,14 @@ _ck_seat_set_kind (CkSeat *seat, seat->priv->kind = kind; } +static void +_ck_seat_set_type_string (CkSeat *seat, + const char *type) +{ + g_free (seat->priv->type); + seat->priv->type = g_strdup (type); +} + static void ck_seat_set_property (GObject *object, guint prop_id, @@ -875,6 +1464,9 @@ ck_seat_set_property (GObject *object, case PROP_KIND: _ck_seat_set_kind (self, g_value_get_enum (value)); break; + case PROP_TYPE: + _ck_seat_set_type_string (self, g_value_get_string (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -896,7 +1488,10 @@ ck_seat_get_property (GObject *object, g_value_set_string (value, self->priv->id); break; case PROP_KIND: - g_value_set_string (value, self->priv->id); + g_value_set_enum (value, self->priv->kind); + break; + case PROP_TYPE: + g_value_set_string (value, self->priv->type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1008,6 +1603,48 @@ ck_seat_class_init (CkSeatClass *klass) g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, CK_TYPE_DEVICE); + signals [REMOVE_REQUEST] = g_signal_new ("remove-request", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, remove_request), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals [OPEN_SESSION_REQUEST] = g_signal_new ("open-session-request", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, open_session_request), + NULL, + NULL, + ck_marshal_VOID__STRING_STRING_STRING_POINTER_STRING_POINTER, + G_TYPE_NONE, + 6, + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_STRING, + G_TYPE_STRING, + CK_DBUS_TYPE_G_STRING_STRING_HASHTABLE, + G_TYPE_STRING, + CK_DBUS_TYPE_G_STRING_STRING_HASHTABLE, + G_TYPE_INVALID); + signals [CLOSE_SESSION_REQUEST] = g_signal_new ("close-session-request", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, close_session_request), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 1, DBUS_TYPE_G_OBJECT_PATH); + signals [NO_RESPAWN] = g_signal_new ("no-respawn", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, no_respawn), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +/* HERE */ g_object_class_install_property (object_class, PROP_ID, @@ -1025,6 +1662,13 @@ ck_seat_class_init (CkSeatClass *klass) CK_SEAT_KIND_DYNAMIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_TYPE, + g_param_spec_string ("type", + "type", + "type", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (CkSeatPrivate)); dbus_g_object_type_install_info (CK_TYPE_SEAT, &dbus_glib_ck_seat_object_info); @@ -1040,6 +1684,7 @@ ck_seat_init (CkSeat *seat) g_free, (GDestroyNotify) g_object_unref); seat->priv->devices = g_ptr_array_new (); + seat->priv->manager_proxy = NULL; } static void @@ -1065,28 +1710,32 @@ ck_seat_finalize (GObject *object) g_ptr_array_free (seat->priv->devices, TRUE); g_hash_table_destroy (seat->priv->sessions); g_free (seat->priv->id); + g_free (seat->priv->type); G_OBJECT_CLASS (ck_seat_parent_class)->finalize (object); } CkSeat * ck_seat_new (const char *sid, - CkSeatKind kind) + CkSeatKind kind, + const char *type) { GObject *object; object = g_object_new (CK_TYPE_SEAT, "id", sid, "kind", kind, + "type", type, NULL); return CK_SEAT (object); } CkSeat * -ck_seat_new_with_devices (const char *sid, - CkSeatKind kind, - GPtrArray *devices) +ck_seat_new_with_devices_and_sessions (const char *sid, + CkSeatKind kind, + GPtrArray *devices, + GPtrArray *sessions) { GObject *object; int i; @@ -1101,27 +1750,62 @@ ck_seat_new_with_devices (const char *sid, ck_seat_add_device (CK_SEAT (object), g_ptr_array_index (devices, i), NULL); } } + if (sessions != NULL) { + for (i = 0; i < sessions->len; i++) { + ck_seat_add_session (CK_SEAT (object), g_ptr_array_index (sessions, i), NULL); + } + } + return CK_SEAT (object); } +static char * +generate_static_session_id (const char *sid, + const char *session_name) +{ + const char *seat_name; + char *ssid; + + seat_name = strrchr (sid, '/'); + + if (seat_name == NULL) { + g_warning ("Seat id '%s' lacks a /", sid); + seat_name = sid; + } else { + seat_name++; + } + + ssid = g_strdup_printf ("%s/Session%s%s", + CK_DBUS_PATH, seat_name, + session_name); + + return ssid; +} + CkSeat * -ck_seat_new_from_file (const char *sid, +ck_seat_new_from_file (char **sid, const char *path) { - GKeyFile *key_file; - gboolean res; - GError *error; - char *group; - CkSeat *seat; - gboolean hidden; - GPtrArray *devices; - char **device_list; - gsize ndevices; - gsize i; + GKeyFile *key_file; + gboolean res; + GError *error; + char *group; + CkSeat *seat; + char *read_sid; + gboolean hidden; + GPtrArray *sessions; + char **session_list; + gsize nsessions; + GPtrArray *devices; + char **device_list; + gsize ndevices; + gsize i; seat = NULL; + g_debug ("CkSeat: New seat from file."); + key_file = g_key_file_new (); error = NULL; res = g_key_file_load_from_file (key_file, @@ -1146,9 +1830,52 @@ ck_seat_new_from_file (const char *sid, goto out; } + read_sid = g_key_file_get_string (key_file, group, "ID", NULL); + if (IS_STR_SET (read_sid)) { + g_free (*sid); + *sid = g_strdup_printf ("%s/%s", CK_DBUS_PATH, read_sid); + } else { + g_free (read_sid); + } + + session_list = g_key_file_get_string_list (key_file, group, "Sessions", &nsessions, NULL); + + sessions = g_ptr_array_sized_new (nsessions); + + for (i = 0; i < nsessions; i++) { + char *path; + char *file; + char *ssid; + CkSession *session; + + file = g_strconcat (session_list[i], ".session", NULL); + path = g_build_filename (CK_SESSION_DIR, file, NULL); + g_free (file); + + /* FIXME: we should probably use the same naming pool as + * sessions generated from ck-manager. We mangle the name + * here so we don't clash with names from ck-manager + */ + ssid = generate_static_session_id (*sid, session_list[i]); + + session = ck_session_new_from_file (ssid, path); + + if (session == NULL) { + g_warning ("Unable to load session from file %s", path); + g_free (path); + continue; + } + g_free (path); + ck_session_set_seat_id (session, *sid, NULL); + + g_ptr_array_add (sessions, session); + } + + g_strfreev (session_list); + device_list = g_key_file_get_string_list (key_file, group, "Devices", &ndevices, NULL); - g_debug ("Creating seat %s with %zd devices", sid, ndevices); + g_debug ("CkSeat: Creating seat %s with %zd devices", *sid, ndevices); devices = g_ptr_array_sized_new (ndevices); @@ -1177,11 +1904,12 @@ ck_seat_new_from_file (const char *sid, g_strfreev (split); } g_strfreev (device_list); - g_free (group); - seat = ck_seat_new_with_devices (sid, CK_SEAT_KIND_STATIC, devices); + seat = ck_seat_new_with_devices_and_sessions (*sid, CK_SEAT_KIND_STATIC, devices, sessions); g_ptr_array_free (devices, TRUE); + g_free (group); + out: g_key_file_free (key_file); @@ -1356,10 +2084,15 @@ ck_seat_dump (CkSeat *seat, error = NULL; if (! ck_session_get_id (seat->priv->active_session, &session_id, &error)) { - g_warning ("Cannot get session id for active session on seat %s: %s", - seat->priv->id, - error->message); - g_error_free (error); + if (error) { + g_warning ("Cannot get session id for active session on seat %s: %s", + seat->priv->id, + error->message); + g_error_free (error); + } else { + g_warning ("Cannot get session id for active session on seat %s", + seat->priv->id); + } } else { g_key_file_set_string (key_file, group_name, @@ -1371,3 +2104,24 @@ ck_seat_dump (CkSeat *seat, g_free (group_name); } + +gboolean +ck_seat_is_managed (CkSeat *seat) +{ + return seat->priv->manager_proxy != NULL; +} + +CkSession * +ck_seat_get_session (CkSeat *seat, + const char *ssid) +{ + CkSession *session; + + session = g_hash_table_lookup (seat->priv->sessions, ssid); + + if (session == NULL) { + return NULL; + } + + return g_object_ref (session); +} diff --git a/src/ck-seat.h b/src/ck-seat.h index fb9a955..c621dd9 100644 --- a/src/ck-seat.h +++ b/src/ck-seat.h @@ -47,6 +47,18 @@ typedef struct { GObjectClass parent_class; + void (* remove_request) (CkSeat *seat); + void (* open_session_request) (CkSeat *seat, + const char *ssid, + const char *session_type, + const char *display_template_name, + GHashTable *display_variables, + const char *display_type, + GHashTable *parameters); + void (* close_session_request) (CkSeat *seat, + const char *ssid); + void (* no_respawn) (CkSeat *seat, + const char *ssid); void (* active_session_changed) (CkSeat *seat, const char *ssid); void (* session_added) (CkSeat *seat, @@ -57,6 +69,11 @@ typedef struct GValueArray *device); void (* device_removed) (CkSeat *seat, GValueArray *device); + void (* session_to_add) (CkSeat *seat, + gboolean is_dynamic, + const char *command); + void (* session_to_remove) (CkSeat *seat, + int display_number); } CkSeatClass; typedef enum @@ -84,12 +101,14 @@ typedef enum GQuark ck_seat_error_quark (void); GType ck_seat_get_type (void); CkSeat * ck_seat_new (const char *sid, - CkSeatKind kind); -CkSeat * ck_seat_new_from_file (const char *sid, - const char *path); -CkSeat * ck_seat_new_with_devices (const char *sid, CkSeatKind kind, - GPtrArray *devices); + const char *type); +CkSeat * ck_seat_new_from_file (char **sid, + const char *path); +CkSeat * ck_seat_new_with_devices_and_sessions (const char *sid, + CkSeatKind kind, + GPtrArray *devices, + GPtrArray *sessions); gboolean ck_seat_register (CkSeat *seat); @@ -104,18 +123,31 @@ void ck_seat_dump (CkSeat *seat, gboolean ck_seat_get_kind (CkSeat *seat, CkSeatKind *kind, GError **error); +gboolean ck_seat_get_type_string (CkSeat *seat, + char **type, + GError **error); gboolean ck_seat_add_session (CkSeat *seat, CkSession *session, GError **error); gboolean ck_seat_remove_session (CkSeat *seat, CkSession *session, GError **error); +gboolean ck_seat_request_open_session (CkSeat *seat, + CkSession *session, + GError **error); +gboolean ck_seat_request_close_session (CkSeat *seat, + CkSession *session, + GError **error); gboolean ck_seat_add_device (CkSeat *seat, GValueArray *device, GError **error); gboolean ck_seat_remove_device (CkSeat *seat, GValueArray *device, GError **error); +gboolean ck_seat_is_managed (CkSeat *seat); +CkSession *ck_seat_get_session (CkSeat *seat, + const char *ssid); +void ck_seat_request_removal (CkSeat *seat); /* exported methods */ gboolean ck_seat_get_id (CkSeat *seat, @@ -137,6 +169,14 @@ gboolean ck_seat_can_activate_sessions (CkSeat *seat, gboolean ck_seat_activate_session (CkSeat *seat, const char *ssid, DBusGMethodInvocation *context); +gboolean ck_seat_manage (CkSeat *seat, + DBusGMethodInvocation *context); +gboolean ck_seat_unmanage (CkSeat *seat, + DBusGMethodInvocation *context); + +gboolean ck_seat_no_respawn (CkSeat *seat, + CkSession *session, + GError **error); G_END_DECLS diff --git a/src/ck-session-leader.c b/src/ck-session-leader.c index c2ab15b..99b81a0 100644 --- a/src/ck-session-leader.c +++ b/src/ck-session-leader.c @@ -237,6 +237,7 @@ static struct { { "x11-display", add_param_string }, { "remote-host-name", add_param_string }, { "session-type", add_param_string }, + { "display-type", add_param_string }, { "is-local", add_param_boolean }, { "unix-user", add_param_int }, }; diff --git a/src/ck-session.c b/src/ck-session.c index f291bf3..e5dc45b 100644 --- a/src/ck-session.c +++ b/src/ck-session.c @@ -40,6 +40,7 @@ #include "ck-session-glue.h" #include "ck-marshal.h" #include "ck-run-programs.h" +#include "ck-display-template.h" #define CK_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SESSION, CkSessionPrivate)) @@ -57,6 +58,7 @@ struct CkSessionPrivate char *seat_id; char *session_type; + char *display_type; char *login_session_id; char *display_device; char *x11_display_device; @@ -64,8 +66,15 @@ struct CkSessionPrivate char *remote_host_name; guint uid; + CkDisplayTemplate *display_template; + GHashTable *display_variables; + gboolean active; gboolean is_local; + gboolean is_open; + gboolean ever_open; + gboolean under_request; + GMutex *mutex_under_request; GTimeVal creation_time; @@ -74,6 +83,8 @@ struct CkSessionPrivate gboolean idle_hint; GTimeVal idle_since_hint; + gboolean remove_on_close; + DBusGConnection *connection; DBusGProxy *bus_proxy; }; @@ -91,17 +102,24 @@ enum { PROP_0, PROP_ID, PROP_COOKIE, + PROP_SEAT_ID, PROP_USER, PROP_UNIX_USER, PROP_X11_DISPLAY, PROP_X11_DISPLAY_DEVICE, PROP_DISPLAY_DEVICE, PROP_SESSION_TYPE, + PROP_DISPLAY_TYPE, + PROP_DISPLAY_TEMPLATE, + PROP_DISPLAY_VARIABLES, PROP_REMOTE_HOST_NAME, PROP_LOGIN_SESSION_ID, PROP_IS_LOCAL, + PROP_IS_OPEN, + PROP_EVER_OPEN, PROP_ACTIVE, PROP_IDLE_HINT, + PROP_REMOVE_ON_CLOSE, }; static guint signals [LAST_SIGNAL] = { 0, }; @@ -127,7 +145,9 @@ static gboolean register_session (CkSession *session) { GError *error = NULL; + GObject *existing_session; + g_debug ("CkSession: Register session."); error = NULL; session->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); if (session->priv->connection == NULL) { @@ -143,6 +163,15 @@ register_session (CkSession *session) DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + existing_session = dbus_g_connection_lookup_g_object (session->priv->connection, + session->priv->id); + + if (existing_session != NULL) { + g_warning ("Session '%s' was registered twice!", + session->priv->id); + return FALSE; + } + dbus_g_connection_register_g_object (session->priv->connection, session->priv->id, G_OBJECT (session)); return TRUE; @@ -300,6 +329,40 @@ ck_session_set_idle_hint (CkSession *session, return TRUE; } +gboolean +session_set_remove_on_close (CkSession *session, + gboolean remove_on_close, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (session->priv->remove_on_close != remove_on_close) { + session->priv->remove_on_close = remove_on_close; + } + + if (session->priv->remove_on_close == TRUE) + g_debug ("CkSession: Setting remove on close to true."); + else + g_debug ("CkSession: Setting remove on close to false."); + + return TRUE; +} + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Session1 \ + org.freedesktop.ConsoleKit.Session.SetRemoveOnClose boolean:TRUE +*/ +gboolean +ck_session_set_remove_on_close (CkSession *session, + gboolean remove_on_close, + DBusGMethodInvocation *context) +{ + return session_set_remove_on_close (session, remove_on_close, NULL); +} + gboolean ck_session_get_idle_hint (CkSession *session, gboolean *idle_hint, @@ -366,6 +429,7 @@ ck_session_activate (CkSession *session, { gboolean res; + g_debug ("CkSession: Session activate."); g_return_val_if_fail (CK_IS_SESSION (session), FALSE); res = FALSE; @@ -396,6 +460,8 @@ ck_session_set_active (CkSession *session, { g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + g_debug ("CkSession: Session set active."); + if (session->priv->active != active) { session->priv->active = active; g_signal_emit (session, signals [ACTIVE_CHANGED], 0, active); @@ -418,6 +484,68 @@ ck_session_set_is_local (CkSession *session, return TRUE; } +gboolean +ck_session_set_is_open (CkSession *session, + gboolean is_open, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (session->priv->is_open != is_open) { + session->priv->is_open = is_open; + session->priv->ever_open = TRUE; + } + + return TRUE; +} + +gboolean +ck_session_set_ever_open (CkSession *session, + gboolean ever_open, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (session->priv->ever_open != ever_open) { + session->priv->ever_open = ever_open; + } + + return TRUE; +} + +static gboolean +timeout_for_under_request (gpointer data) +{ + CkSession *session = CK_SESSION (data); + g_mutex_lock (session->priv->mutex_under_request); + session->priv->under_request = FALSE; + g_mutex_unlock (session->priv->mutex_under_request); + + g_debug ("CkSession: timeout for under request of session %s", session->priv->id); + return FALSE; +} + +gboolean +ck_session_set_under_request (CkSession *session, + gboolean under_request, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + g_mutex_lock (session->priv->mutex_under_request); + if (!under_request) { + session->priv->under_request = FALSE; + } else { + if (!session->priv->under_request) { + session->priv->under_request = TRUE; + g_timeout_add_seconds (1, timeout_for_under_request, session); + } + } + g_mutex_unlock (session->priv->mutex_under_request); + + return TRUE; +} + gboolean ck_session_get_id (CkSession *session, char **id, @@ -553,6 +681,20 @@ ck_session_get_creation_time (CkSession *session, return TRUE; } +gboolean +ck_session_get_remove_on_close (CkSession *session, + gboolean *remove_on_close, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (remove_on_close != NULL) { + *remove_on_close = session->priv->remove_on_close; + } + + return TRUE; +} + gboolean ck_session_get_session_type (CkSession *session, char **type, @@ -567,6 +709,20 @@ ck_session_get_session_type (CkSession *session, return TRUE; } +gboolean +ck_session_get_display_type (CkSession *session, + char **type, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (type != NULL) { + *type = g_strdup (session->priv->display_type); + } + + return TRUE; +} + gboolean ck_session_is_active (CkSession *session, gboolean *active, @@ -595,6 +751,50 @@ ck_session_is_local (CkSession *session, return TRUE; } +gboolean +ck_session_is_open (CkSession *session, + gboolean *open, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (open != NULL) { + *open = session->priv->is_open; + } + + return TRUE; +} + +gboolean +ck_session_get_ever_open (CkSession *session, + gboolean *open, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (open != NULL) { + *open = session->priv->ever_open; + } + + return TRUE; +} + +gboolean +ck_session_get_under_request (CkSession *session, + gboolean *under_request, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (under_request != NULL) { + g_mutex_lock (session->priv->mutex_under_request); + *under_request = session->priv->under_request; + g_mutex_unlock (session->priv->mutex_under_request); + } + + return TRUE; +} + gboolean ck_session_set_id (CkSession *session, const char *id, @@ -608,6 +808,21 @@ ck_session_set_id (CkSession *session, return TRUE; } +static void +ck_session_set_display_variables (CkSession *session, + GHashTable *display_variables) +{ + if (session->priv->display_variables != NULL) { + g_hash_table_unref (session->priv->display_variables); + } + + if (display_variables != NULL) { + session->priv->display_variables = g_hash_table_ref (display_variables); + } else { + session->priv->display_variables = NULL; + } +} + gboolean ck_session_set_cookie (CkSession *session, const char *cookie, @@ -724,6 +939,54 @@ ck_session_set_session_type (CkSession *session, return TRUE; } +gboolean +ck_session_set_display_type (CkSession *session, + const char *type, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + g_free (session->priv->display_type); + session->priv->display_type = g_strdup (type); + + return TRUE; +} + +static gboolean +ck_session_set_display_template (CkSession *session, + CkDisplayTemplate *display_template, + GError **error) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + if (session->priv->display_template != NULL) { + g_object_unref (session->priv->display_template); + } + + if (display_template != NULL) { + session->priv->display_template = g_object_ref (display_template); + ck_session_set_display_type (session, ck_display_template_get_name (display_template), error); + } else { + session->priv->display_template = NULL; + } + + return TRUE; +} + +GHashTable * +ck_session_get_display_variables (CkSession *session) +{ + return g_hash_table_ref (session->priv->display_variables); +} + +CkDisplayTemplate * +ck_session_get_display_template (CkSession *session) +{ + g_return_val_if_fail (CK_IS_SESSION (session), FALSE); + + return g_object_ref (session->priv->display_template); +} + static void ck_session_set_property (GObject *object, guint prop_id, @@ -741,15 +1004,33 @@ ck_session_set_property (GObject *object, case PROP_IS_LOCAL: ck_session_set_is_local (self, g_value_get_boolean (value), NULL); break; + case PROP_IS_OPEN: + ck_session_set_is_open (self, g_value_get_boolean (value), NULL); + break; + case PROP_EVER_OPEN: + ck_session_set_ever_open (self, g_value_get_boolean (value), NULL); + break; case PROP_ID: ck_session_set_id (self, g_value_get_string (value), NULL); break; case PROP_COOKIE: ck_session_set_cookie (self, g_value_get_string (value), NULL); break; + case PROP_SEAT_ID: + ck_session_set_seat_id (self, g_value_get_string (value), NULL); + break; case PROP_SESSION_TYPE: ck_session_set_session_type (self, g_value_get_string (value), NULL); break; + case PROP_DISPLAY_TYPE: + ck_session_set_display_type (self, g_value_get_string (value), NULL); + break; + case PROP_DISPLAY_TEMPLATE: + ck_session_set_display_template (self, g_value_get_object (value), NULL); + break; + case PROP_DISPLAY_VARIABLES: + ck_session_set_display_variables (self, g_value_get_boxed (value)); + break; case PROP_X11_DISPLAY: ck_session_set_x11_display (self, g_value_get_string (value), NULL); break; @@ -774,6 +1055,9 @@ ck_session_set_property (GObject *object, case PROP_IDLE_HINT: session_set_idle_hint_internal (self, g_value_get_boolean (value)); break; + case PROP_REMOVE_ON_CLOSE: + session_set_remove_on_close (self, g_value_get_boolean (value), NULL); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -797,15 +1081,33 @@ ck_session_get_property (GObject *object, case PROP_IS_LOCAL: g_value_set_boolean (value, self->priv->is_local); break; + case PROP_IS_OPEN: + g_value_set_boolean (value, self->priv->is_open); + break; + case PROP_EVER_OPEN: + g_value_set_boolean (value, self->priv->ever_open); + break; case PROP_ID: g_value_set_string (value, self->priv->id); break; case PROP_COOKIE: g_value_set_string (value, self->priv->cookie); break; + case PROP_SEAT_ID: + g_value_set_string (value, self->priv->seat_id); + break; case PROP_SESSION_TYPE: g_value_set_string (value, self->priv->session_type); break; + case PROP_DISPLAY_TYPE: + g_value_set_string (value, self->priv->display_type); + break; + case PROP_DISPLAY_TEMPLATE: + g_value_set_object (value, self->priv->display_template); + break; + case PROP_DISPLAY_VARIABLES: + g_value_set_boxed (value, self->priv->display_variables); + break; case PROP_X11_DISPLAY: g_value_set_string (value, self->priv->x11_display); break; @@ -830,6 +1132,9 @@ ck_session_get_property (GObject *object, case PROP_IDLE_HINT: g_value_set_boolean (value, self->priv->idle_hint); break; + case PROP_REMOVE_ON_CLOSE: + g_value_set_boolean (value, self->priv->remove_on_close); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -881,6 +1186,8 @@ session_add_activity_watch (CkSession *session) static void session_remove_activity_watch (CkSession *session) { + g_debug ("CkSession: Remove activity watch."); + if (session->priv->idle_monitor == NULL) { return; } @@ -999,6 +1306,13 @@ ck_session_class_init (CkSessionClass *klass) "cookie", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_SEAT_ID, + g_param_spec_string ("seat-id", + "seat id", + "seat id", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SESSION_TYPE, @@ -1007,6 +1321,27 @@ ck_session_class_init (CkSessionClass *klass) "session type", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_DISPLAY_TYPE, + g_param_spec_string ("display-type", + "session-type", + "session type", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_DISPLAY_TEMPLATE, + g_param_spec_object ("display-template", + "Display Template", + "The display template", + CK_TYPE_DISPLAY_TEMPLATE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_DISPLAY_VARIABLES, + g_param_spec_boxed ("display-variables", + "Display Variables", + "Display type specific variables", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_LOGIN_SESSION_ID, g_param_spec_string ("login-session-id", @@ -1070,6 +1405,14 @@ ck_session_class_init (CkSessionClass *klass) FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_REMOVE_ON_CLOSE, + g_param_spec_boolean ("remove-on-close", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_type_class_add_private (klass, sizeof (CkSessionPrivate)); dbus_g_object_type_install_info (CK_TYPE_SESSION, &dbus_glib_ck_session_object_info); @@ -1080,8 +1423,16 @@ ck_session_init (CkSession *session) { session->priv = CK_SESSION_GET_PRIVATE (session); + session->priv->display_variables = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + /* FIXME: should we have a property for this? */ g_get_current_time (&session->priv->creation_time); + + if (!session->priv->mutex_under_request) + session->priv->mutex_under_request = g_mutex_new (); } static void @@ -1104,6 +1455,7 @@ ck_session_finalize (GObject *object) g_free (session->priv->cookie); g_free (session->priv->seat_id); g_free (session->priv->session_type); + g_free (session->priv->display_type); g_free (session->priv->login_session_id); g_free (session->priv->display_device); g_free (session->priv->x11_display_device); @@ -1113,17 +1465,139 @@ ck_session_finalize (GObject *object) G_OBJECT_CLASS (ck_session_parent_class)->finalize (object); } +CkSession * +ck_session_new_from_file (const char *ssid, + const char *path) +{ + GKeyFile *key_file; + gboolean res; + GError *error; + char *group; + char *name; + gboolean hidden; + char *type; + char *display_template_string; + CkSession *session; + GHashTable *display_variables; + char **type_keys; + + g_debug ("CkSession: New session from file."); + key_file = g_key_file_new (); + error = NULL; + res = g_key_file_load_from_file (key_file, + path, + G_KEY_FILE_NONE, + &error); + + if (! res) { + g_warning ("Unable to load sessions from file %s: %s", + path, error->message); + g_error_free (error); + return NULL; + } + + group = g_key_file_get_start_group (key_file); + if (group == NULL || strcmp (group, "Session Entry") != 0) { + g_warning ("Not a session file: %s", path); + g_key_file_free (key_file); + return NULL; + } + + hidden = g_key_file_get_boolean (key_file, group, "Hidden", NULL); + + if (hidden) { + g_debug ("CkSession: Session is hidden"); + g_free (group); + g_key_file_free (key_file); + return NULL; + } + + name = g_key_file_get_string (key_file, group, "Name", NULL); + + if (name == NULL) { + g_warning ("Session file %s doesn't contain a name", path); + g_free (group); + g_key_file_free (key_file); + return NULL; + } + + type = g_key_file_get_string (key_file, group, "Type", NULL); + + if (type == NULL) { + g_warning ("Session file %s doesn't contain a type", path); + g_free (group); + g_key_file_free (key_file); + return NULL; + } + + display_template_string = g_key_file_get_string (key_file, group, "DisplayTemplate", NULL); + + if (display_template_string == NULL) { + g_warning ("Session file %s doesn't contain a display type", path); + g_free (group); + g_key_file_free (key_file); + return NULL; + } + + /* Find a group in the key file named after the display type and stuff + * all its entries into a hash table for later. + * + * Those keys are for things like the display number and the vt to + * run X with. + */ + display_variables = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + type_keys = g_key_file_get_keys (key_file, display_template_string, NULL, NULL); + + if (type_keys != NULL) { + int i; + for (i = 0; type_keys[i] != NULL; i++) { + char *string; + string = g_key_file_get_string (key_file, display_template_string, type_keys[i], NULL); + + g_hash_table_insert (display_variables, g_strdup (type_keys[i]), string); + } + g_strfreev (type_keys); + } + + session = ck_session_new (ssid, type, display_template_string, display_variables); + + g_free (display_template_string); + g_free (group); + g_free (type); + g_hash_table_unref (display_variables); + + return session; +} + CkSession * ck_session_new (const char *ssid, - const char *cookie) + const char *type, + const char *display_template_string, + GHashTable *display_variables) { + CkDisplayTemplate *display_template; GObject *object; gboolean res; + g_debug ("CkSession: New Session."); + + display_template = ck_display_template_get_from_name (display_template_string); + + if (display_template == NULL) { + g_warning ("Unable to load display type %s", display_template_string); + return NULL; + } + object = g_object_new (CK_TYPE_SESSION, "id", ssid, - "cookie", cookie, + "session-type", type, + "display-type", display_template_string, + "display-template", display_template, + "display-variables", display_variables, NULL); + res = register_session (CK_SESSION (object)); if (! res) { g_object_unref (object); @@ -1138,9 +1612,80 @@ ck_session_new (const char *ssid, G_TYPE_VALUE, \ G_TYPE_INVALID)) +void +ck_session_set_parameters (CkSession *session, + const GPtrArray *parameters) +{ + int i; + GObjectClass *class; + GType object_type; + + object_type = CK_TYPE_SESSION; + class = g_type_class_ref (object_type); + for (i = 0; i < parameters->len; i++) { + gboolean res; + GValue val_struct = { 0, }; + GValue value = { 0, }; + char *prop_name; + GValue *prop_val; + GParamSpec *pspec; + + g_value_init (&val_struct, CK_TYPE_PARAMETER_STRUCT); + g_value_set_static_boxed (&val_struct, g_ptr_array_index (parameters, i)); + + res = dbus_g_type_struct_get (&val_struct, + 0, &prop_name, + 1, &prop_val, + G_MAXUINT); + if (! res) { + g_debug ("CkSession: Unable to extract parameter input"); + goto cont; + } + + if (prop_name == NULL) { + g_debug ("CkSession: Skipping NULL parameter"); + goto cont; + } + + if (strcmp (prop_name, "id") == 0 + || strcmp (prop_name, "cookie") == 0) { + g_debug ("CkSession: Skipping restricted parameter: %s", prop_name); + goto cont; + } + + pspec = g_object_class_find_property (class, prop_name); + if (! pspec) { + g_debug ("CkSession: Skipping unknown parameter: %s", prop_name); + goto cont; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) { + g_debug ("CkSession: property '%s' is not writable", pspec->name); + goto cont; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + res = g_value_transform (prop_val, &value); + if (! res) { + g_debug ("CkSession: unable to transform property value for '%s'", pspec->name); + goto cont; + } + + g_object_set_property (G_OBJECT (session), prop_name, &value); + g_value_unset (&value); + cont: + g_free (prop_name); + if (prop_val != NULL) { + g_value_unset (prop_val); + g_free (prop_val); + } + } + + g_type_class_unref (class); +} + CkSession * ck_session_new_with_parameters (const char *ssid, - const char *cookie, const GPtrArray *parameters) { GObject *object; @@ -1152,6 +1697,8 @@ ck_session_new_with_parameters (const char *ssid, GObjectClass *class; GType object_type; + g_debug ("CkSession: New session with parameters"); + object_type = CK_TYPE_SESSION; class = g_type_class_ref (object_type); @@ -1169,12 +1716,6 @@ ck_session_new_with_parameters (const char *ssid, g_value_set_string (¶ms[n_params].value, ssid); n_params++; - params[n_params].name = g_strdup ("cookie"); - params[n_params].value.g_type = 0; - g_value_init (¶ms[n_params].value, G_TYPE_STRING); - g_value_set_string (¶ms[n_params].value, cookie); - n_params++; - if (parameters != NULL) { for (i = 0; i < parameters->len; i++) { gboolean res; @@ -1259,7 +1800,7 @@ ck_session_run_programs (CkSession *session, const char *action) { int n; - char *extra_env[11]; /* be sure to adjust this as needed */ + char *extra_env[12]; /* be sure to adjust this as needed */ n = 0; @@ -1267,6 +1808,9 @@ ck_session_run_programs (CkSession *session, if (session->priv->session_type != NULL) { extra_env[n++] = g_strdup_printf ("CK_SESSION_TYPE=%s", session->priv->session_type); } + if (session->priv->display_type != NULL) { + extra_env[n++] = g_strdup_printf ("CK_SESSION_DISPLAY_TYPE=%s", session->priv->display_type); + } extra_env[n++] = g_strdup_printf ("CK_SESSION_SEAT_ID=%s", session->priv->seat_id); extra_env[n++] = g_strdup_printf ("CK_SESSION_USER_UID=%d", session->priv->uid); if (session->priv->display_device != NULL && strlen (session->priv->display_device) > 0) { @@ -1301,6 +1845,10 @@ ck_session_dump (CkSession *session, char *s; char *group_name; + if (!session->priv->is_open) { + return; + } + group_name = g_strdup_printf ("Session %s", session->priv->id); g_key_file_set_integer (key_file, group_name, "uid", session->priv->uid); g_key_file_set_string (key_file, @@ -1313,6 +1861,12 @@ ck_session_dump (CkSession *session, "type", NONULL_STRING (session->priv->session_type)); } + if (session->priv->display_type != NULL) { + g_key_file_set_string (key_file, + group_name, + "display_type", + NONULL_STRING (session->priv->display_type)); + } if (session->priv->login_session_id != NULL && strlen (session->priv->login_session_id) > 0) { g_key_file_set_string (key_file, group_name, diff --git a/src/ck-session.h b/src/ck-session.h index b6b565b..a24d0a8 100644 --- a/src/ck-session.h +++ b/src/ck-session.h @@ -25,6 +25,8 @@ #include #include +#include "ck-display-template.h" + G_BEGIN_DECLS #define CK_TYPE_SESSION (ck_session_get_type ()) @@ -68,10 +70,15 @@ typedef enum GQuark ck_session_error_quark (void); GType ck_session_get_type (void); +CkSession * ck_session_new_from_file (const char *ssid, + const char *path); CkSession * ck_session_new (const char *ssid, - const char *cookie); + const char *type, + const char *display_type_string, + GHashTable *display_variables); CkSession * ck_session_new_with_parameters (const char *ssid, - const char *cookie, + const GPtrArray *parameters); +void ck_session_set_parameters (CkSession *session, const GPtrArray *parameters); void ck_session_dump (CkSession *session, @@ -86,6 +93,15 @@ gboolean ck_session_set_active (CkSession *se gboolean ck_session_set_is_local (CkSession *session, gboolean is_local, GError **error); +gboolean ck_session_set_is_open (CkSession *session, + gboolean is_open, + GError **error); +gboolean ck_session_set_ever_open (CkSession *session, + gboolean ever_open, + GError **error); +gboolean ck_session_set_under_request (CkSession *session, + gboolean under_request, + GError **error); gboolean ck_session_set_id (CkSession *session, const char *ssid, GError **error); @@ -116,6 +132,11 @@ gboolean ck_session_set_remote_host_name (CkSession *se gboolean ck_session_set_session_type (CkSession *session, const char *type, GError **error); +gboolean ck_session_set_display_type (CkSession *session, + const char *type, + GError **error); +GHashTable *ck_session_get_display_variables (CkSession *session); +CkDisplayTemplate *ck_session_get_display_template (CkSession *session); /* Exported methods */ @@ -132,6 +153,15 @@ gboolean ck_session_is_active (CkSession *se gboolean ck_session_is_local (CkSession *session, gboolean *local, GError **error); +gboolean ck_session_is_open (CkSession *session, + gboolean *open, + GError **error); +gboolean ck_session_get_ever_open (CkSession *session, + gboolean *open, + GError **error); +gboolean ck_session_get_under_request (CkSession *session, + gboolean *under_request, + GError **error); gboolean ck_session_get_unix_user (CkSession *session, guint *uid, GError **error); @@ -150,12 +180,18 @@ gboolean ck_session_get_login_session_id (CkSession *se gboolean ck_session_get_session_type (CkSession *session, char **type, GError **error); +gboolean ck_session_get_display_type (CkSession *session, + char **type, + GError **error); gboolean ck_session_get_remote_host_name (CkSession *session, char **host_name, GError **error); gboolean ck_session_get_creation_time (CkSession *session, char **iso8601_datetime, GError **error); +gboolean ck_session_get_remove_on_close (CkSession *session, + gboolean *remove_on_close, + GError **error); /*deprecated*/ gboolean ck_session_get_user (CkSession *session, guint *uid, @@ -171,6 +207,12 @@ gboolean ck_session_get_idle_since_hint (CkSession *se gboolean ck_session_set_idle_hint (CkSession *session, gboolean idle_hint, DBusGMethodInvocation *context); +gboolean session_set_remove_on_close (CkSession *session, + gboolean remove_on_close, + GError **error); +gboolean ck_session_set_remove_on_close (CkSession *session, + gboolean remove_on_close, + DBusGMethodInvocation *context); /* Privileged actions */ gboolean ck_session_activate (CkSession *session, diff --git a/src/org.freedesktop.ConsoleKit.Manager.xml b/src/org.freedesktop.ConsoleKit.Manager.xml index f903b55..cf9b6b3 100644 --- a/src/org.freedesktop.ConsoleKit.Manager.xml +++ b/src/org.freedesktop.ConsoleKit.Manager.xml @@ -160,6 +160,23 @@ + + + + an array of unmanaged Seat IDs + + + + + This gets a list of the unmanaged Seats + that are statically configured under /etc/ConsoleKit/seats.d + Each Seat ID is an D-Bus object path for the object that implements the + Seat interface. + + org.freedesktop.ConsoleKit.Seat + + + @@ -196,6 +213,22 @@ + + + + + + The session id of the session + + + + + This may be used to set Remove On Close for a Session. + + + + + @@ -310,12 +343,115 @@ + + + + The type of seat to add + + + + + The Seat ID of the added seat + + + + + This method is to create a new seat + + + + + + + + The type of seat to add + + + + + The Seat ID of to be added seat + + + + + This method is to create a new seat by specify given sid + + + + + + + + + The Seat ID of the seat to remove + + + + + This method is to remove a seat + + + + + + + + + The seat to add the session to + + + + + The type of session to run (e.g. "LoginWindow", "Chooser", etc) + + + + + The name of the display type to use (defined in displays.d) + + + + + Session type specific parameters + + + + + Session ID + + + + + Request a new session gets added to seat. + + + + + + + + The session id of the session to remove + + + + + This method is to remove a session from a seat + + + + + The Seat ID for the added seat + + + The type of seat added. + + Emitted when a Seat has been added to the system. diff --git a/src/org.freedesktop.ConsoleKit.Seat.xml b/src/org.freedesktop.ConsoleKit.Seat.xml index d95990b..acefe29 100644 --- a/src/org.freedesktop.ConsoleKit.Seat.xml +++ b/src/org.freedesktop.ConsoleKit.Seat.xml @@ -100,6 +100,24 @@ seat at a time. + + + + + Attempt to create unmanaged sessions for this seat. + + + + + + + + + Stop managing seat. + + + + @@ -160,5 +178,73 @@ seat at a time. + + + + The session id of the session to add + + + + + The type of session to run (e.g. "LoginWindow", "Chooser", etc) + + + + + The name of display template + + + + + Session type specific parameters + + + + + The type of display to use (e.g. "X11", "Command", "XDMCP", etc) + + + + + Display type specific parameters + + + + + Emitted when a new session should get added to the seat. + + + + + + + The session id of the session to remove + + + + + Emitted when a session with given display number need to be removed. + + + + + + + The session id of the session to not respawn + + + + + Emitted when ck-seat-tool indicates a session is to be removed. + + + + + + + Emitted when seat needs to get removed. + + + diff --git a/src/org.freedesktop.ConsoleKit.Session.xml b/src/org.freedesktop.ConsoleKit.Session.xml index b6e1cdb..2652058 100644 --- a/src/org.freedesktop.ConsoleKit.Session.xml +++ b/src/org.freedesktop.ConsoleKit.Session.xml @@ -52,6 +52,19 @@ session-type + + + + Display type + + + + + Returns the display type of the session. + + display-type + + @@ -174,6 +187,18 @@ is-local + + + + TRUE if the session is open, otherwise FALSE + + + + Returns whether the session is open + + is-open + + @@ -275,6 +300,21 @@ + + + + + boolean value to set the remove-on-close to + + + + + This may be used by the session to indicate that + it should be respawn or not when it is closed. + + + + @@ -317,6 +357,19 @@ + + + + The id of the session. + + The object path of the session. Typically this is set by a session leader during a call to the + OpenSessionWithParameters method, when + opening a session in response to the OpenSessionRequest + signal. + + + + @@ -342,6 +395,16 @@ + + + + The display type of the session. + Indicate the display template name. All the display template configuration + files are under /etc/ConsoleKit/displays.d/. + + + + @@ -396,6 +459,19 @@ + + + + + Whether the session is open + Sessions added from static configuration or in direct response to a call to + the AddSession method are initialally + closed and aren't open until a call to the OpenSessionWithParameters + method. + + + + @@ -408,6 +484,14 @@ + + + + + Whether the session is dynamic + + + @@ -430,6 +514,14 @@ + + + + + Whether the session respawn when it is closed + + + diff --git a/src/strverscmp.c b/src/strverscmp.c new file mode 100644 index 0000000..f077651 --- /dev/null +++ b/src/strverscmp.c @@ -0,0 +1,131 @@ +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2000, 2002, 2004, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jean-François Bignolles , 1997. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#if !_LIBC +# include +#endif + +#include +#include + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractional parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x4 +#define S_F 0x8 +#define S_Z 0xC + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* ISDIGIT differs from isdigit, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char + or EOF. + - It's typically faster. + POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to + isdigit unless it's important to use the locale's definition + of `digit' even when the host does not conform to POSIX. */ +#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) + +#undef __strverscmp +#undef strverscmp + +#ifndef weak_alias +# define __strverscmp strverscmp +#endif + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the texinfo doc). +*/ + +int +__strverscmp (const char *s1, const char *s2) +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + int state; + int diff; + + /* Symbol(s) 0 [1-9] others (padding) + Transition (10) 0 (01) d (00) x (11) - */ + static const unsigned int next_state[] = + { + /* state x d 0 - */ + /* S_N */ S_N, S_I, S_Z, S_N, + /* S_I */ S_N, S_I, S_I, S_I, + /* S_F */ S_N, S_F, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z, S_Z + }; + + static const int result_type[] = + { + /* state x/x x/d x/0 x/- d/x d/d d/0 d/- + 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + + /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, CMP, 1, LEN, LEN, CMP, + 1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, + /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, 1, 1, CMP, -1, CMP, CMP, CMP, + -1, CMP, CMP, CMP + }; + + if (p1 == p2) + return 0; + + c1 = *p1++; + c2 = *p2++; + /* Hint: '0' is a digit too. */ + state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0)); + + while ((diff = c1 - c2) == 0 && c1 != '\0') + { + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state |= (c1 == '0') + (ISDIGIT (c1) != 0); + } + + state = result_type[state << 2 | ((c2 == '0') + (ISDIGIT (c2) != 0))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while (ISDIGIT (*p1++)) + if (!ISDIGIT (*p2++)) + return 1; + + return ISDIGIT (*p2) ? -1 : diff; + + default: + return state; + } +} +#ifdef weak_alias +weak_alias (__strverscmp, strverscmp) +#endif diff --git a/src/strverscmp.h b/src/strverscmp.h new file mode 100644 index 0000000..48670c8 --- /dev/null +++ b/src/strverscmp.h @@ -0,0 +1,25 @@ +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2000, 2002, 2004, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jean-François Bignolles , 1997. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef STRVERSCMP_H_ +# define STRVERSCMP_H_ + +int strverscmp (const char *, const char *); + +#endif /* not STRVERSCMP_H_ */ diff --git a/tools/Makefile.am b/tools/Makefile.am index 13c191f..fa16c68 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -54,6 +54,7 @@ sbin_PROGRAMS = \ ck-log-system-start \ ck-log-system-restart \ ck-log-system-stop \ + ck-seat-tool \ $(NULL) ck_launch_session_SOURCES = \ @@ -83,6 +84,14 @@ ck_history_LDADD = \ $(top_builddir)/src/libck-event-log.la \ $(NULL) +ck_seat_tool_SOURCES = \ + ck-seat-tool.c \ + $(NULL) + +ck_seat_tool_LDADD = \ + $(CONSOLE_KIT_LIBS) \ + $(NULL) + ck_log_system_start_SOURCES = \ ck-log-system-start.c \ $(NULL) diff --git a/tools/ck-seat-tool.c b/tools/ck-seat-tool.c new file mode 100644 index 0000000..347c81c --- /dev/null +++ b/tools/ck-seat-tool.c @@ -0,0 +1,449 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Sun Microsystems, 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 + * 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. + * + * Authors: Halton Huo + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CK_NAME "org.freedesktop.ConsoleKit" +#define CK_PATH "/org/freedesktop/ConsoleKit" +#define CK_INTERFACE "org.freedesktop.ConsoleKit" +#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager" +#define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat" +#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session" + +#define CK_DBUS_TYPE_G_STRING_STRING_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)) + +#define IS_STR_SET(x) (x != NULL && x[0] != '\0') +#define CK_PATH_PREFIX "/org/freedesktop/ConsoleKit/" + +static gboolean add = FALSE; +static gboolean delete = FALSE; +static gboolean show_version = FALSE; +static char *session_type = NULL; +static char *display_type = NULL; +static char *seat_id = NULL; +static char *session_id = NULL; +static gchar **remaining_args = NULL; + +static const GOptionEntry options [] = { + { "add", 'a', 0, G_OPTION_ARG_NONE, &add, N_("Add a new session"), NULL}, + { "session-type", '\0', 0, G_OPTION_ARG_STRING, &session_type, N_("Specify session type when adding a session. Default is LoginWindow."), NULL}, + { "display-type", '\0', 0, G_OPTION_ARG_STRING, &display_type, N_("Specify display type under /ConsoleKit/displays.d/ when adding a session."), NULL}, + { "seat-id", '\0', 0, G_OPTION_ARG_STRING, &seat_id, N_("Specify seat id when adding a session. If not given, create a new seat."), NULL}, + { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args, N_("Specify values of variables in display type. For example display=:10"), NULL }, + { "delete", 'd', 0, G_OPTION_ARG_NONE, &delete, N_("Delete a session"), NULL}, + { "session-id", '\0', 0, G_OPTION_ARG_STRING, &session_id, N_("Specify session id when deleting a session"), NULL}, + { "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, + { NULL } +}; + +static void +add_session (DBusGConnection *connection) +{ + DBusGProxy *mgr_proxy = NULL; + DBusGProxy *seat_proxy = NULL; + GError *error = NULL; + gboolean res; + char *sid = NULL; + GPtrArray *seats; + char *ssid = NULL; + int i; + gboolean found; + char *sstype = NULL; + GHashTable *variables = NULL; + + if (! IS_STR_SET (session_type)) { + sstype = g_strdup ("LoginWindow"); + } else { + sstype = g_strdup (session_type); + } + + mgr_proxy = dbus_g_proxy_new_for_name (connection, + CK_NAME, + CK_MANAGER_PATH, + CK_MANAGER_INTERFACE); + if (mgr_proxy == NULL) { + return; + } + + if (! IS_STR_SET(seat_id)) { + + /* If seat id is not given, create a new seat */ + error = NULL; + res = dbus_g_proxy_call (mgr_proxy, + "AddSeat", + &error, + G_TYPE_STRING, "Default", + G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &sid, + G_TYPE_INVALID); + if (!res) { + g_warning ("Unable to add seat: %s", error->message); + g_error_free (error); + g_object_unref (mgr_proxy); + return; + } + + } else { + if (!g_str_has_prefix (seat_id, CK_PATH_PREFIX)) { + sid = g_strdup_printf ("%s%s", CK_PATH_PREFIX, seat_id); + } else { + sid = g_strdup (seat_id); + } + /* Check whether seat is existing, if not, try to create it. */ + + error = NULL; + res = dbus_g_proxy_call (mgr_proxy, + "GetSeats", + &error, + G_TYPE_INVALID, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), + &seats, + G_TYPE_INVALID); + if (!res) { + g_warning ("Unable to get seat list: %s", error->message); + g_error_free (error); + g_object_unref (mgr_proxy); + return; + } + + found = FALSE; + for (i = 0; i < seats->len; i++) { + char *tmp_sid; + + tmp_sid = g_ptr_array_index (seats, i); + if (g_str_equal (sid, tmp_sid)) { + found = TRUE; + g_free (tmp_sid); + break; + } + + g_free (tmp_sid); + } + + if (! found) { + error = NULL; + res = dbus_g_proxy_call (mgr_proxy, + "AddSeatById", + &error, + G_TYPE_STRING, "Default", + DBUS_TYPE_G_OBJECT_PATH, sid, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (!res) { + g_warning ("Unable to add seat: %s", error->message); + g_error_free (error); + g_object_unref (mgr_proxy); + return; + } + } + } + + seat_proxy = dbus_g_proxy_new_for_name (connection, + CK_NAME, + sid, + CK_SEAT_INTERFACE); + + if (seat_proxy == NULL) { + g_warning ("Failed to talk to seat '%s'", sid); + g_object_unref (mgr_proxy); + return; + } + + variables = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + + if (remaining_args) { + for (i = 0; i < G_N_ELEMENTS (remaining_args); i++) { + char **arr; + + /* split var=value */ + arr = g_strsplit (remaining_args [i], "=", 2); + if (arr[0] && arr[1]) { + g_hash_table_insert (variables, + g_strdup(arr[0]), + g_strdup (arr[1])); + } + g_strfreev (arr); + } + } + + error = NULL; + res = dbus_g_proxy_call (mgr_proxy, + "AddSession", + &error, + DBUS_TYPE_G_OBJECT_PATH, sid, + G_TYPE_STRING, sstype, + G_TYPE_STRING, display_type, + CK_DBUS_TYPE_G_STRING_STRING_HASHTABLE, variables, + G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &ssid, + G_TYPE_INVALID); + + if (!res) { + g_warning ("Unable to add dynamic session: %s", error->message); + g_error_free (error); + } else { + dbus_g_proxy_call_no_reply (seat_proxy, + "Manage", + G_TYPE_INVALID, + G_TYPE_INVALID); + g_print ("Seat %s with session %s has been added\n", sid, ssid); + } + + g_object_unref (seat_proxy); + g_object_unref (mgr_proxy); +} + +static gboolean +is_session_on_seat (DBusGConnection *connection, + const char *sid, + const char *ssid, + gboolean *is_last_session) +{ + + DBusGProxy *seat_proxy = NULL; + GPtrArray *sessions = NULL; + char *ssid_tmp = NULL; + gboolean res; + gboolean retval = FALSE; + int i; + GError *error = NULL; + + seat_proxy = dbus_g_proxy_new_for_name (connection, + CK_NAME, + sid, + CK_SEAT_INTERFACE); + + if (seat_proxy == NULL) { + g_warning ("Failed to talk to seat '%s'", sid); + return FALSE; + } + + error = NULL; + res = dbus_g_proxy_call (seat_proxy, + "GetSessions", + &error, + G_TYPE_INVALID, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), + &sessions, + G_TYPE_INVALID); + if (! res) { + g_warning ("Failed to get list of sessions for %s: %s", sid, error->message); + g_error_free (error); + g_object_unref (seat_proxy); + return FALSE; + } + + for (i = 0; i < sessions->len; i++) { + + ssid_tmp = g_ptr_array_index (sessions, i); + + if (g_str_equal (ssid, ssid_tmp)) { + retval = TRUE; + break; + } + + g_free (ssid_tmp); + ssid_tmp = NULL; + } + + if (is_last_session != NULL) { + *is_last_session = sessions->len == 1; + } + + g_ptr_array_free (sessions, TRUE); + g_object_unref (seat_proxy); + + return retval; +} + +static char * +find_seat_id_from_session_id (DBusGConnection *connection, + DBusGProxy *proxy, + const char *ssid, + gboolean *is_last_session) +{ + GError *error; + GPtrArray *seats; + int i; + char *sid; + gboolean res; + + error = NULL; + res = dbus_g_proxy_call (proxy, + "GetSeats", + &error, + G_TYPE_INVALID, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), + &seats, + G_TYPE_INVALID); + + if (! res) { + g_warning ("Failed to get list of seats: %s", error->message); + g_error_free (error); + return NULL; + } + + for (i = 0; i < seats->len; i++) { + + sid = g_ptr_array_index (seats, i); + if (is_session_on_seat (connection, sid, ssid, is_last_session)) { + break; + } + + g_free (sid); + } + g_ptr_array_free (seats, TRUE); + + return sid; +} + +static void +delete_session (DBusGConnection *connection) +{ + DBusGProxy *proxy; + char *ssid; + char *sid; + gboolean is_last_session; + + if (!g_str_has_prefix (session_id, CK_PATH_PREFIX)) { + ssid = g_strdup_printf ("%s%s", CK_PATH_PREFIX, session_id); + } else { + ssid = g_strdup (session_id); + } + + proxy = dbus_g_proxy_new_for_name (connection, + CK_NAME, + CK_MANAGER_PATH, + CK_MANAGER_INTERFACE); + if (proxy == NULL) { + return; + } + + sid = find_seat_id_from_session_id (connection, proxy, ssid, &is_last_session); + + dbus_g_proxy_call_no_reply (proxy, + "WillNotRespawn", + DBUS_TYPE_G_OBJECT_PATH, ssid, + G_TYPE_INVALID, + G_TYPE_INVALID); + + dbus_g_proxy_call_no_reply (proxy, + "RemoveSession", + DBUS_TYPE_G_OBJECT_PATH, ssid, + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (is_last_session) { + dbus_g_proxy_call_no_reply (proxy, + "RemoveSeat", + DBUS_TYPE_G_OBJECT_PATH, sid, + G_TYPE_INVALID, + G_TYPE_INVALID); + } + + g_object_unref (proxy); +} + +int +main (int argc, char *argv[]) +{ + DBusGConnection *connection; + GOptionContext *ctx; + GError *error = NULL; + gboolean res; + + g_type_init (); + + /* Option parsing */ + ctx = g_option_context_new (_("- Manage dynamic sessions")); + g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE); + res = g_option_context_parse (ctx, &argc, &argv, &error); + + if (!res) { + if (error) { + g_warning ("%s", error->message); + g_error_free (error); + } + exit (1); + } + + g_option_context_free (ctx); + + if (show_version) { + g_print ("%s %s\n", argv[0], VERSION); + exit (0); + } + + if (add && delete) { + g_warning ("Can not specify -a and -d at the same time!"); + exit (1); + } + + if (!add && !delete) { + g_warning ("Must specify -a, -d!"); + exit (1); + } + + if (delete && (! IS_STR_SET (session_id))) { + g_warning ("You must specify session id for deleting a session. You can get all sessions by ck-list-sessions"); + exit (1); + } + + if (add && (! IS_STR_SET (display_type)) ) { + g_warning ("You must specify display type for adding a session. You can get all display types under /ConsoleKit/displays.d/"); + g_warning ("Invalid display type!"); + exit (1); + } + + error = NULL; + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + g_message ("Failed to connect to the D-Bus daemon: %s", error->message); + g_error_free (error); + exit (1); + } + + + if (add) { + add_session (connection); + } else if (delete) { + delete_session (connection); + } else { + g_warning ("Invaild parameters!"); + exit (1); + } + + return 0; +} diff --git a/tools/list-sessions.c b/tools/list-sessions.c index c3a020b..1230f76 100644 --- a/tools/list-sessions.c +++ b/tools/list-sessions.c @@ -45,6 +45,23 @@ #define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat" #define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session" +#define IS_STR_SET(x) (x != NULL && x[0] != '\0') + +typedef struct CkSessionOutput { + char *prop_name; + char *prop_value; +} CkSessionOutput; + +static gboolean do_all = FALSE; +static gboolean do_version = FALSE; +static char *do_format = NULL; +static GOptionEntry entries [] = { + { "all", 'a', 0, G_OPTION_ARG_NONE, &do_all, N_("List all sessions. If not given, only list open sessions"), NULL }, + { "format", 'f', 0, G_OPTION_ARG_STRING, &do_format, N_("Prints information according to the given format"), NULL }, + { "version", 'V', 0, G_OPTION_ARG_NONE, &do_version, N_("Version of this application"), NULL }, + { NULL } +}; + static gboolean get_uint (DBusGProxy *proxy, const char *method, @@ -175,6 +192,7 @@ list_session (DBusGConnection *connection, char *sid; char *lsid; char *session_type; + char *display_type; char *x11_display; char *x11_display_device; char *display_device; @@ -183,8 +201,11 @@ list_session (DBusGConnection *connection, char *idle_since_hint; gboolean is_active; gboolean is_local; + gboolean is_open; char *short_sid; const char *short_ssid; + char **format_arr = NULL; + int i, j; proxy = dbus_g_proxy_new_for_name (connection, CK_NAME, @@ -197,6 +218,7 @@ list_session (DBusGConnection *connection, sid = NULL; lsid = NULL; session_type = NULL; + display_type = NULL; x11_display = NULL; x11_display_device = NULL; display_device = NULL; @@ -208,15 +230,21 @@ list_session (DBusGConnection *connection, get_path (proxy, "GetSeatId", &sid); get_string (proxy, "GetLoginSessionId", &lsid); get_string (proxy, "GetSessionType", &session_type); + get_string (proxy, "GetDisplayType", &display_type); get_string (proxy, "GetX11Display", &x11_display); get_string (proxy, "GetX11DisplayDevice", &x11_display_device); get_string (proxy, "GetDisplayDevice", &display_device); get_string (proxy, "GetRemoteHostName", &remote_host_name); + get_boolean (proxy, "IsOpen", &is_open); get_boolean (proxy, "IsActive", &is_active); get_boolean (proxy, "IsLocal", &is_local); get_string (proxy, "GetCreationTime", &creation_time); get_string (proxy, "GetIdleSinceHint", &idle_since_hint); + if (!do_all && !is_open) { + return; + } + realname = get_real_name (uid); short_sid = sid; @@ -229,24 +257,49 @@ list_session (DBusGConnection *connection, short_ssid = ssid + strlen (CK_PATH) + 1; } - printf ("%s:\n\tunix-user = '%d'\n\trealname = '%s'\n\tseat = '%s'\n\tsession-type = '%s'\n\tactive = %s\n\tx11-display = '%s'\n\tx11-display-device = '%s'\n\tdisplay-device = '%s'\n\tremote-host-name = '%s'\n\tis-local = %s\n\ton-since = '%s'\n\tlogin-session-id = '%s'", - short_ssid, - uid, - realname, - short_sid, - session_type, - is_active ? "TRUE" : "FALSE", - x11_display, - x11_display_device, - display_device, - remote_host_name, - is_local ? "TRUE" : "FALSE", - creation_time, - lsid); - if (idle_since_hint != NULL && idle_since_hint[0] != '\0') { - printf ("\n\tidle-since-hint = '%s'", idle_since_hint); + CkSessionOutput output[] = { + {"session-id", g_strdup (short_ssid)}, + {"unix-user", g_strdup_printf ("%d", uid)}, + {"realname", g_strdup (realname)}, + {"seat", g_strdup (short_sid)}, + {"session-type", g_strdup (session_type)}, + {"display-type", g_strdup (display_type)}, + {"open", is_open ? "TRUE" : "FALSE"}, + {"active", is_active ? "TRUE" : "FALSE"}, + {"x11-display", g_strdup (x11_display)}, + {"x11-display-device", g_strdup (x11_display_device)}, + {"display-device", g_strdup (display_device)}, + {"remote-host-name", g_strdup (remote_host_name)}, + {"is-local", is_local ? "TRUE" : "FALSE"}, + {"on-since", g_strdup (creation_time)}, + {"login-session-id", g_strdup (lsid)}, + {"idle-since-hint", g_strdup (idle_since_hint)}, + }; + + if (IS_STR_SET (do_format)) { + format_arr = g_strsplit (do_format, ",", -1); + + for (i = 0; format_arr[i] != NULL; ++i) { + for (j = 0; j < G_N_ELEMENTS (output); j++) { + if (g_str_equal (format_arr[i], output[j].prop_name)) { + printf ("'%s'\t", output[j].prop_value); + break; + } + } + } + printf ("\n"); + g_strfreev (format_arr); + + } else { + for (j = 0; j < G_N_ELEMENTS (output); j++) { + if (g_str_equal (output[j].prop_name, "session-id")) + printf ("%s:\n", output[j].prop_value); + else + printf ("\t%s = '%s'\n", + output[j].prop_name, + output[j].prop_value); + } } - printf ("\n"); g_free (idle_since_hint); g_free (creation_time); @@ -255,9 +308,11 @@ list_session (DBusGConnection *connection, g_free (sid); g_free (lsid); g_free (session_type); + g_free (display_type); g_free (x11_display); g_free (x11_display_device); g_free (display_device); + g_object_unref (proxy); } @@ -367,11 +422,6 @@ main (int argc, GOptionContext *context; gboolean retval; GError *error = NULL; - static gboolean do_version = FALSE; - static GOptionEntry entries [] = { - { "version", 'V', 0, G_OPTION_ARG_NONE, &do_version, N_("Version of this application"), NULL }, - { NULL } - }; g_type_init (); -- cgit v1.2.3