[Spice-devel] [spice-xpi 08/12] Use glib functions to spawn/watch client

Christophe Fergeau cfergeau at redhat.com
Tue Mar 12 04:23:03 PDT 2013


This removes quite a lot of OS-specific code.
---
 SpiceXPI/src/plugin/controller-unix.cpp | 143 +++++++++++++++++---------------
 SpiceXPI/src/plugin/controller.h        |   7 +-
 configure.ac                            |   2 +-
 3 files changed, 85 insertions(+), 67 deletions(-)

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 0435c24..7b4bc1c 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -163,94 +163,107 @@ uint32_t SpiceController::Write(const void *lpBuffer, uint32_t nBytesToWrite)
     return len;
 }
 
-bool SpiceController::StartClient()
+void SpiceController::ChildExited(GPid pid, gint status, gpointer user_data)
 {
-    std::string socket_file(m_tmp_dir);
-    socket_file += "/spice-xpi";
+    SpiceController *fake_this = (SpiceController *)user_data;
 
-    /* use a pipe for the children to wait until it gets tracked */
-    int pipe_fds[2] = { -1, -1 };
-    if (pipe(pipe_fds) < 0) {
-        perror("spice-xpi system error");
-        return false;
-    }
-
-    m_pid_controller = fork();
-    if (m_pid_controller == 0)
-    {
-        setpgrp();
+    g_message("Client with pid %p exited", pid);
 
-        close(pipe_fds[1]);
-        pipe_fds[1] = -1;
+    g_main_loop_quit(fake_this->m_child_watch_mainloop);
+    /* FIXME: we are not in the main thread!! */
+    fake_this->m_plugin->OnSpiceClientExit(status);
+}
 
-        char c;
-        if (read(pipe_fds[0], &c, 1) != 0)
-            g_critical("Error while reading on pipe: %s", g_strerror(errno));
+void SpiceController::WaitForPid(GPid pid)
+{
+    GMainContext *context;
+    GSource *source;
 
-        close(pipe_fds[0]);
-        pipe_fds[0] = -1;
+    context = g_main_context_new();
 
-        gchar **env = g_get_environ();
-        env = g_environ_setenv(env, "SPICE_XPI_SOCKET", socket_file.c_str(), TRUE);
-        if (!m_proxy.empty())
-            env = g_environ_setenv(env, "SPICE_PROXY", m_proxy.c_str(), TRUE);
+    m_child_watch_mainloop = g_main_loop_new(context, FALSE);
+    source = g_child_watch_source_new(pid);
+    g_source_set_callback(source, (GSourceFunc)ChildExited, this, NULL);
+    g_source_attach(source, context);
 
-        execle("/usr/libexec/spice-xpi-client",
-               "/usr/libexec/spice-xpi-client", NULL,
-               env);
-        g_message("failed to run spice-xpi-client, running spicec instead");
+    g_main_loop_run(m_child_watch_mainloop);
 
-        // TODO: temporary fallback for backward compatibility
-        execle("/usr/bin/spicec",
-               "/usr/bin/spicec", "--controller", NULL,
-               env);
+    g_main_loop_unref(m_child_watch_mainloop);
+    g_main_context_unref(context);
 
-        g_critical("ERROR failed to run spicec fallback");
-        g_strfreev(env);
-        exit(EXIT_FAILURE);
-    }
-    else
-    {
-        g_debug("child pid: %"G_GUINT64_FORMAT, (guint64)m_pid_controller);
+    g_spawn_close_pid(pid);
+    if (pid == m_pid_controller)
+        m_pid_controller = 0;
+}
 
-        close(pipe_fds[0]);
-        pipe_fds[0] = -1;
 
-        pthread_t controller_thread_id;
-        pthread_create(&controller_thread_id, NULL, ControllerWaitHelper,
-            reinterpret_cast<void*>(this));
+gpointer SpiceController::ClientThread(gpointer data)
+{
+    char *spice_xpi_argv[] = { "/usr/libexec/spice-xpi-client", NULL };
+    SpiceController *fake_this = (SpiceController *)data;
+    gchar **env = g_get_environ();
+    GPid pid;
+    gboolean spawned;
+    GError *error = NULL;
+
+    std::string socket_file(fake_this->m_tmp_dir);
+    socket_file += "/spice-xpi";
 
-        close(pipe_fds[1]);
-        pipe_fds[1] = -1;
+    fake_this->SetFilename(socket_file);
 
-        this->SetFilename(socket_file);
+    env = g_environ_setenv(env, "SPICE_XPI_SOCKET", socket_file.c_str(), TRUE);
+    if (!fake_this->m_proxy.empty())
+        env = g_environ_setenv(env, "SPICE_PROXY", fake_this->m_proxy.c_str(), TRUE);
 
-        return true;
+    spawned = g_spawn_async(NULL,
+                            spice_xpi_argv, env,
+                            G_SPAWN_DO_NOT_REAP_CHILD,
+                            NULL, NULL, /* child_func, child_arg */
+                            &pid, &error);
+    if (error != NULL) {
+        g_warning("failed to start spice-xpi-client: %s", error->message);
+        g_clear_error(&error);
+    }
+    if (!spawned) {
+        // TODO: temporary fallback for backward compatibility
+        char *spicec_argv[] = { "/usr/bin/spicec", "--controller", NULL };
+        g_message("failed to run spice-xpi-client, running spicec instead");
+        spawned = g_spawn_async(NULL, spicec_argv, env,
+                                G_SPAWN_DO_NOT_REAP_CHILD,
+                                NULL, NULL, /* child_func, child_arg */
+                                &pid, &error);
+    }
+    if (error != NULL) {
+        g_warning("failed to start spice-xpi-client: %s", error->message);
+        g_clear_error(&error);
+    }
+    g_strfreev(env);
+    if (!spawned) {
+        g_critical("ERROR failed to run spicec fallback");
+        return NULL;
     }
 
-    g_return_val_if_reached(false);
-}
+#ifdef XP_UNIX
+    fake_this->m_pid_controller = pid;
+#endif
+    fake_this->WaitForPid(pid);
 
-void SpiceController::StopClient()
-{
-    if (m_pid_controller > 0)
-        kill(-m_pid_controller, SIGTERM);
+    return NULL;
 }
 
-void *SpiceController::ControllerWaitHelper(void *opaque)
+bool SpiceController::StartClient()
 {
-    SpiceController *fake_this = reinterpret_cast<SpiceController *>(opaque);
-    if (!fake_this)
-        return NULL;
+    GThread *thread;
 
-    int exit_code;
-    waitpid(fake_this->m_pid_controller, &exit_code, 0);
-    g_debug("child finished, pid: %"G_GUINT64_FORMAT, (guint64)exit_code);
+    thread = g_thread_create(ClientThread, this, FALSE, NULL);
 
-    fake_this->m_plugin->OnSpiceClientExit(exit_code);
-    fake_this->m_pid_controller = -1;
+    return (thread != NULL);
+}
 
-    return NULL;
+void SpiceController::StopClient()
+{
+    if (m_pid_controller > 0)
+        kill(-m_pid_controller, SIGTERM);
 }
 
 int SpiceController::TranslateRC(int nRC)
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index 54d1d46..268994b 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -58,6 +58,7 @@
             (rather than anonymous pipe which is local only and one way)
 */
 
+#include <glib.h>
 #include <string>
 extern "C" {
 #  include <stdint.h>
@@ -86,7 +87,9 @@ public:
 
 private:
     int Connect();
-    static void *ControllerWaitHelper(void *opaque);
+    void WaitForPid(GPid pid);
+    static void ChildExited(GPid pid, gint status, gpointer user_data);
+    static gpointer ClientThread(gpointer data);
 
     nsPluginInstance *m_plugin;
     int m_client_socket;
@@ -94,6 +97,8 @@ private:
     std::string m_tmp_dir;
     pid_t m_pid_controller;
     std::string m_proxy;
+
+    GMainLoop *m_child_watch_mainloop;
 };
 
 #endif // SPICE_CONTROLLER_H
diff --git a/configure.ac b/configure.ac
index 48d3a6b..3ed0cfc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,7 +24,7 @@ AC_CONFIG_SUBDIRS([spice-protocol])
 SPICE_PROTOCOL_CFLAGS='-I ${top_srcdir}/spice-protocol'
 AC_SUBST(SPICE_PROTOCOL_CFLAGS)
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 gio-2.0)
+PKG_CHECK_MODULES(GLIB, glib-2.0 gio-2.0 gthread-2.0)
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
-- 
1.8.1.4



More information about the Spice-devel mailing list