[gnome-session] gsm: Make it possible for a .session file to define a fallback session



commit 8e6713f15a2819dd7bdd8a9a33ec2314633ff2d6
Author: Vincent Untz <vuntz gnome org>
Date:   Mon Nov 29 19:28:07 2010 +0100

    gsm: Make it possible for a .session file to define a fallback session
    
    Two new keys are introduced: IsRunnableHelper and FallbackSession.
    
    The first one defines a helper that is run to know if the session can be
    used (only if the helper returns 0). The second defines the fallback
    session to use if the helper doesn't return 0.
    
    The helper cannot run for more than 500 ms.
    
    There is a test program to make sure that our helper process management
    works okay.

 gnome-session/Makefile.am           |    8 ++-
 gnome-session/gsm-process-helper.c  |  142 +++++++++++++++++++++++++++++++++++
 gnome-session/gsm-process-helper.h  |   33 ++++++++
 gnome-session/gsm-session-fill.c    |   49 ++++++++++++-
 gnome-session/test-process-helper.c |   58 ++++++++++++++
 5 files changed, 288 insertions(+), 2 deletions(-)
---
diff --git a/gnome-session/Makefile.am b/gnome-session/Makefile.am
index f25c4b7..030052e 100644
--- a/gnome-session/Makefile.am
+++ b/gnome-session/Makefile.am
@@ -2,7 +2,8 @@ bin_PROGRAMS = gnome-session
 noinst_LTLIBRARIES = libgsmutil.la
 noinst_PROGRAMS = 		\
 	test-client-dbus	\
-	test-inhibit
+	test-inhibit		\
+	test-process-helper
 
 AM_CPPFLAGS =					\
 	$(GNOME_SESSION_CFLAGS)			\
@@ -48,6 +49,8 @@ gnome_session_SOURCES =				\
 	gsm-inhibitor.c				\
 	gsm-manager.c				\
 	gsm-manager.h				\
+	gsm-process-helper.c			\
+	gsm-process-helper.h			\
 	gsm-session-fill.c			\
 	gsm-session-fill.h			\
 	gsm-session-save.c			\
@@ -95,6 +98,9 @@ test_inhibit_LDADD = $(GNOME_SESSION_LIBS)
 test_client_dbus_SOURCES = test-client-dbus.c
 test_client_dbus_LDADD = $(DBUS_GLIB_LIBS)
 
+test_process_helper_SOURCES = test-process-helper.c gsm-process-helper.c gsm-process-helper.h
+test_process_helper_LDADD = $(DBUS_GLIB_LIBS)
+
 gsm-marshal.c: gsm-marshal.list
 	$(AM_V_GEN)echo "#include \"gsm-marshal.h\"" > $@ && \
 	$(GLIB_GENMARSHAL) $< --prefix=gsm_marshal --body >> $@
diff --git a/gnome-session/gsm-process-helper.c b/gnome-session/gsm-process-helper.c
new file mode 100644
index 0000000..c36fe2a
--- /dev/null
+++ b/gnome-session/gsm-process-helper.c
@@ -0,0 +1,142 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "gsm-process-helper.h"
+
+typedef struct {
+        const char *command_line;
+        GPid        pid;
+        gboolean    proper_exit;
+        int         status;
+        GMainLoop  *loop;
+        guint       child_id;
+        guint       timeout_id;
+} GsmProcessHelper;
+
+static void
+on_child_exited_simple (GPid     pid,
+                        gint     status,
+                        gpointer data)
+{
+        g_spawn_close_pid (pid);
+}
+
+static void
+on_child_exited (GPid     pid,
+                 gint     status,
+                 gpointer data)
+{
+        GsmProcessHelper *helper = data;
+
+        helper->proper_exit = TRUE;
+        helper->status = status;
+
+        g_spawn_close_pid (pid);
+        g_main_loop_quit (helper->loop);
+}
+
+static gboolean
+on_child_timeout (gpointer data)
+{
+        GsmProcessHelper *helper = data;
+
+        kill (helper->pid, SIGTERM);
+        g_warning ("Had to kill '%s' helper", helper->command_line);
+
+        helper->proper_exit = FALSE;
+        g_main_loop_quit (helper->loop);
+
+        return FALSE;
+}
+
+int
+gsm_process_helper (const char   *command_line,
+                    unsigned int  timeout)
+{
+        GsmProcessHelper *helper;
+        gchar **argv = NULL;
+        GPid pid;
+        gboolean ret;
+        int exit_status = -1;
+
+        if (!g_shell_parse_argv (command_line, NULL, &argv, NULL))
+                return -1;
+
+        ret = g_spawn_async (NULL,
+                             argv,
+                             NULL,
+                             G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD,
+                             NULL,
+                             NULL,
+                             &pid,
+                             NULL);
+
+        g_strfreev (argv);
+
+        if (!ret)
+                return -1;
+
+        helper = g_slice_new0 (GsmProcessHelper);
+
+        helper->command_line = command_line;
+        helper->pid = pid;
+        helper->proper_exit = FALSE;
+        helper->status = -1;
+
+        helper->loop = g_main_loop_new (NULL, FALSE);
+        helper->child_id = g_child_watch_add (helper->pid, on_child_exited, helper);
+        helper->timeout_id = g_timeout_add (timeout, on_child_timeout, helper);
+
+        g_main_loop_run (helper->loop);
+
+        if (helper->proper_exit && WIFEXITED (helper->status))
+                exit_status = WEXITSTATUS (helper->status);
+        else if (!helper->proper_exit) {
+                /* we'll need to call g_spawn_close_pid when the helper exits */
+                g_child_watch_add (pid, on_child_exited_simple, NULL);
+        }
+
+        if (helper->loop) {
+                g_main_loop_unref (helper->loop);
+                helper->loop = NULL;
+        }
+
+        if (helper->child_id) {
+                g_source_remove (helper->child_id);
+                helper->child_id = 0;
+        }
+
+        if (helper->timeout_id) {
+                g_source_remove (helper->timeout_id);
+                helper->timeout_id = 0;
+        }
+
+        g_slice_free (GsmProcessHelper, helper);
+
+        return exit_status;
+}
diff --git a/gnome-session/gsm-process-helper.h b/gnome-session/gsm-process-helper.h
new file mode 100644
index 0000000..f7aa18c
--- /dev/null
+++ b/gnome-session/gsm-process-helper.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GSM_PROCESS_HELPER_H
+#define __GSM_PROCESS_HELPER_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+int  gsm_process_helper (const char   *command_line,
+                         unsigned int  timeout);
+
+G_END_DECLS
+
+#endif /* __GSM_PROCESS_HELPER_H */
diff --git a/gnome-session/gsm-session-fill.c b/gnome-session/gsm-session-fill.c
index 12825cd..c7ee61f 100644
--- a/gnome-session/gsm-session-fill.c
+++ b/gnome-session/gsm-session-fill.c
@@ -25,14 +25,19 @@
 
 #include "gsm-consolekit.h"
 #include "gsm-manager.h"
+#include "gsm-process-helper.h"
 #include "gsm-util.h"
 
 #define GSM_DEFAULT_SESSION "gnome"
 
 #define GSM_KEYFILE_SESSION_GROUP "GNOME Session"
+#define GSM_KEYFILE_RUNNABLE_KEY "IsRunnableHelper"
+#define GSM_KEYFILE_FALLBACK_KEY "FallbackSession"
 #define GSM_KEYFILE_REQUIRED_KEY "Required"
 #define GSM_KEYFILE_DEFAULT_KEY "DefaultApps"
 
+#define GSM_RUNNABLE_HELPER_TIMEOUT 500 /* ms */
+
 /* This doesn't contain the required components, so we need to always
  * call append_required_apps() after a call to append_default_apps(). */
 static void
@@ -302,6 +307,48 @@ find_valid_session_keyfile (const char *session)
         return keyfile;
 }
 
+static GKeyFile *
+get_session_keyfile (const char *session)
+{
+        GKeyFile *keyfile;
+        gboolean  session_runnable;
+        char     *value;
+
+        g_debug ("fill: *** Getting session '%s'", session);
+
+        keyfile = find_valid_session_keyfile (session);
+
+        session_runnable = TRUE;
+
+        value = g_key_file_get_string (keyfile,
+                                       GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_RUNNABLE_KEY,
+                                       NULL);
+        if (!IS_STRING_EMPTY (value)) {
+                g_debug ("fill: *** Launching helper '%s' to know if session is runnable", value);
+                session_runnable = (gsm_process_helper (value, GSM_RUNNABLE_HELPER_TIMEOUT) == 0);
+        }
+        g_free (value);
+
+        if (session_runnable)
+                return keyfile;
+
+        g_debug ("fill: *** Session is not runnable");
+
+        /* We can't run this session, so try to use the fallback */
+        value = g_key_file_get_string (keyfile,
+                                       GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_FALLBACK_KEY,
+                                       NULL);
+
+        g_key_file_free (keyfile);
+        keyfile = NULL;
+
+        if (!IS_STRING_EMPTY (value))
+                keyfile = get_session_keyfile (value);
+        g_free (value);
+
+        return keyfile;
+}
+
 gboolean
 gsm_session_fill (GsmManager  *manager,
                   char       **override_autostart_dirs,
@@ -317,7 +364,7 @@ gsm_session_fill (GsmManager  *manager,
         if (IS_STRING_EMPTY (session))
                 session = GSM_DEFAULT_SESSION;
 
-        keyfile = find_valid_session_keyfile (session);
+        keyfile = get_session_keyfile (session);
 
         if (!keyfile)
                 return FALSE;
diff --git a/gnome-session/test-process-helper.c b/gnome-session/test-process-helper.c
new file mode 100644
index 0000000..50f79ad
--- /dev/null
+++ b/gnome-session/test-process-helper.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 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 <stdlib.h>
+
+#include "gsm-process-helper.h"
+
+int
+main (int   argc,
+      char *argv[])
+{
+        int   ret;
+        char *command_line = "xeyes";
+        int   timeout = 500;
+
+        if (argc > 3) {
+                g_printerr ("Too many arguments.\n");
+                g_printerr ("Usage: %s [COMMAND] [TIMEOUT]\n", argv[0]);
+                return 1;
+        }
+
+        if (argc >= 2)
+                command_line = argv[1];
+        if (argc >= 3) {
+                int i = atoi (argv[2]);
+                if (i > 0)
+                        timeout = i;
+        }
+
+        ret = gsm_process_helper (command_line, timeout);
+
+        if (ret < 0)
+                g_print ("Command did not succeed (or takes too much time).\n");
+        else
+                g_print ("Command returned %d.\n", ret);
+
+        return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]