[Spice-commits] 12 commits - SpiceXPI/src configure.ac

Christophe Fergau teuf at kemper.freedesktop.org
Thu Mar 14 03:10:42 PDT 2013


 SpiceXPI/src/plugin/Makefile.am         |    2 
 SpiceXPI/src/plugin/controller-unix.cpp |  172 ++++++++++++++++++++++
 SpiceXPI/src/plugin/controller-unix.h   |   97 ++++++++++++
 SpiceXPI/src/plugin/controller.cpp      |  190 ++++++++++++++++--------
 SpiceXPI/src/plugin/controller.h        |   41 ++++-
 SpiceXPI/src/plugin/plugin.cpp          |  248 ++++++++++++--------------------
 SpiceXPI/src/plugin/plugin.h            |   10 -
 configure.ac                            |    2 
 8 files changed, 532 insertions(+), 230 deletions(-)

New commits:
commit 8d7d036a97c5c70136d37956506ab0386b3cb549
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Mar 12 16:47:54 2013 +0100

    Add an abstract controller base class
    
    It contains all the generic code, then platform-specific controller
    implementations will inherit from it and overload the non-generic parts
    abstract methods.

diff --git a/SpiceXPI/src/plugin/Makefile.am b/SpiceXPI/src/plugin/Makefile.am
index 2f12e70..2db218e 100644
--- a/SpiceXPI/src/plugin/Makefile.am
+++ b/SpiceXPI/src/plugin/Makefile.am
@@ -26,8 +26,10 @@ libnsISpicec_la_SOURCES =			\
 	$(top_srcdir)/common/rederrorcodes.h	\
 	glib-compat.c				\
 	glib-compat.h				\
+	controller.cpp				\
 	controller.h				\
 	controller-unix.cpp			\
+	controller-unix.h			\
 	npapi/npapi.h				\
 	npapi/npfunctions.h			\
 	npapi/npruntime.h			\
diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 04257e9..1e60e5c 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -40,6 +40,7 @@
  *   the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
+#include "config.h"
 
 #include <cstdio>
 #include <cstdlib>
@@ -57,11 +58,11 @@ extern "C" {
 }
 
 #include "rederrorcodes.h"
-#include "controller.h"
+#include "controller-unix.h"
 #include "plugin.h"
 
-SpiceController::SpiceController(nsPluginInstance *aPlugin):
-    m_plugin(aPlugin),
+SpiceControllerUnix::SpiceControllerUnix(nsPluginInstance *aPlugin):
+    SpiceController(aPlugin),
     m_client_socket(-1)
 {
     // create temporary directory in /tmp
@@ -69,7 +70,7 @@ SpiceController::SpiceController(nsPluginInstance *aPlugin):
     m_tmp_dir = mkdtemp(tmp_dir);
 }
 
-SpiceController::~SpiceController()
+SpiceControllerUnix::~SpiceControllerUnix()
 {
     g_debug(G_STRFUNC);
     Disconnect();
@@ -78,17 +79,7 @@ SpiceController::~SpiceController()
     rmdir(m_tmp_dir.c_str());
 }
 
-void SpiceController::SetFilename(const std::string &name)
-{
-    m_name = name;
-}
-
-void SpiceController::SetProxy(const std::string &proxy)
-{
-    m_proxy = proxy;
-}
-
-int SpiceController::Connect()
+int SpiceControllerUnix::Connect()
 {
     // check, if we have a filename for socket to create
     if (m_name.empty())
@@ -122,20 +113,8 @@ int SpiceController::Connect()
     return rc;
 }
 
-int SpiceController::Connect(const int nRetries)
+bool SpiceControllerUnix::CheckPipe()
 {
-    int rc = -1;
-    int sleep_time = 0;
-
-    // try to connect for specified count
-    for (int i = 0; rc != 0 && i < nRetries; ++i)
-    {
-        rc = Connect();
-        g_usleep(sleep_time * G_USEC_PER_SEC);
-        ++sleep_time;
-    }
-
-    return rc;
 }
 
 GStrv SpiceControllerUnix::GetClientPath()
@@ -152,7 +131,7 @@ GStrv SpiceControllerUnix::GetFallbackClientPath()
     return g_strdupv((GStrv)fallback_argv);
 }
 
-void SpiceController::SetupControllerPipe(GStrv &env)
+void SpiceControllerUnix::SetupControllerPipe(GStrv &env)
 {
     std::string socket_file(this->m_tmp_dir);
     socket_file += "/spice-xpi";
@@ -162,18 +141,13 @@ void SpiceController::SetupControllerPipe(GStrv &env)
     env = g_environ_setenv(env, "SPICE_XPI_SOCKET", socket_file.c_str(), TRUE);
 }
 
-void SpiceController::Disconnect()
+void SpiceControllerUnix::StopClient()
 {
-    // close the socket
-    close(m_client_socket);
-    m_client_socket = -1;
-
-    // delete the temporary file, which is used for the socket
-    unlink(m_name.c_str());
-    m_name.clear();
+    if (m_pid_controller > 0)
+        kill(-m_pid_controller, SIGTERM);
 }
 
-uint32_t SpiceController::Write(const void *lpBuffer, uint32_t nBytesToWrite)
+uint32_t SpiceControllerUnix::Write(const void *lpBuffer, uint32_t nBytesToWrite)
 {
     ssize_t len = send(m_client_socket, lpBuffer, nBytesToWrite, 0);
 
@@ -186,164 +160,13 @@ uint32_t SpiceController::Write(const void *lpBuffer, uint32_t nBytesToWrite)
     return len;
 }
 
-void SpiceController::ChildExited(GPid pid, gint status, gpointer user_data)
-{
-    SpiceController *fake_this = (SpiceController *)user_data;
-
-    g_message("Client with pid %p exited", pid);
-
-    g_main_loop_quit(fake_this->m_child_watch_mainloop);
-    /* FIXME: we are not in the main thread!! */
-    fake_this->m_plugin->OnSpiceClientExit(status);
-}
-
-void SpiceController::WaitForPid(GPid pid)
-{
-    GMainContext *context;
-    GSource *source;
-
-    context = g_main_context_new();
-
-    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);
-
-    g_main_loop_run(m_child_watch_mainloop);
-
-    g_main_loop_unref(m_child_watch_mainloop);
-    g_main_context_unref(context);
-
-    g_spawn_close_pid(pid);
-    if (pid == m_pid_controller)
-        m_pid_controller = 0;
-}
-
-
-gpointer SpiceController::ClientThread(gpointer data)
-{
-    SpiceController *fake_this = (SpiceController *)data;
-    gchar **env = g_get_environ();
-    GPid pid;
-    gboolean spawned = FALSE;
-    GError *error = NULL;
-    GStrv client_argv;
-
-    // Setup client environment
-    fake_this->SetupControllerPipe(env);
-    if (!fake_this->m_proxy.empty())
-        env = g_environ_setenv(env, "SPICE_PROXY", fake_this->m_proxy.c_str(), TRUE);
-
-    // Try to spawn main client
-    client_argv = fake_this->GetClientPath();
-    if (client_argv != NULL) {
-        char *argv_str = g_strjoinv(" ", client_argv);
-        g_warning("main client cmdline: %s", argv_str);
-        g_free(argv_str);
-
-        spawned = g_spawn_async(NULL,
-                                client_argv, env,
-                                G_SPAWN_DO_NOT_REAP_CHILD,
-                                NULL, NULL, /* child_func, child_arg */
-                                &pid, &error);
-        if (error != NULL) {
-            g_warning("failed to start %s: %s", client_argv[0], error->message);
-            g_warn_if_fail(spawned == FALSE);
-            g_clear_error(&error);
-        }
-        g_strfreev(client_argv);
-    }
-
-    if (!spawned) {
-        // Fallback client for backward compatibility
-        GStrv fallback_argv;
-        char *argv_str;
-        fallback_argv = fake_this->GetFallbackClientPath();
-        if (fallback_argv == NULL) {
-            goto out;
-        }
-
-        argv_str = g_strjoinv(" ", fallback_argv);
-        g_warning("fallback client cmdline: %s", argv_str);
-        g_free(argv_str);
-
-        g_message("failed to run preferred client, running fallback client instead");
-        spawned = g_spawn_async(NULL, fallback_argv, env,
-                                G_SPAWN_DO_NOT_REAP_CHILD,
-                                NULL, NULL, /* child_func, child_arg */
-                                &pid, &error);
-        if (error != NULL) {
-            g_warning("failed to start %s: %s", fallback_argv[0], error->message);
-            g_warn_if_fail(spawned == FALSE);
-            g_clear_error(&error);
-        }
-    }
-
-out:
-    g_strfreev(env);
-
-    if (!spawned) {
-        g_critical("ERROR failed to run spicec fallback");
-        return NULL;
-    }
-
-#ifdef XP_UNIX
-    fake_this->m_pid_controller = pid;
-#endif
-    fake_this->WaitForPid(pid);
-
-    return NULL;
-}
-
-bool SpiceController::StartClient()
-{
-    GThread *thread;
-
-    thread = g_thread_new("spice-xpi client thread", ClientThread, this);
-
-    return (thread != NULL);
-}
-
-void SpiceController::StopClient()
-{
-    if (m_pid_controller > 0)
-        kill(-m_pid_controller, SIGTERM);
-}
-
-int SpiceController::TranslateRC(int nRC)
+void SpiceControllerUnix::Disconnect()
 {
-    switch (nRC)
-    {
-    case SPICEC_ERROR_CODE_SUCCESS:
-        return 0;
-
-    case SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED:
-        return RDP_ERROR_CODE_HOST_NOT_FOUND;
-
-    case SPICEC_ERROR_CODE_CONNECT_FAILED:
-        return RDP_ERROR_CODE_WINSOCK_CONNECT_FAILED;
-
-    case SPICEC_ERROR_CODE_ERROR:
-    case SPICEC_ERROR_CODE_SOCKET_FAILED:
-        return RDP_ERROR_CODE_INTERNAL_ERROR;
-
-    case SPICEC_ERROR_CODE_RECV_FAILED:
-        return RDP_ERROR_RECV_WINSOCK_FAILED;
-
-    case SPICEC_ERROR_CODE_SEND_FAILED:
-        return RDP_ERROR_SEND_WINSOCK_FAILED;
-
-    case SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY:
-        return RDP_ERROR_CODE_OUT_OF_MEMORY;
-
-    case SPICEC_ERROR_CODE_AGENT_TIMEOUT:
-        return RDP_ERROR_CODE_TIMEOUT;
-
-    case SPICEC_ERROR_CODE_AGENT_ERROR:
-        return RDP_ERROR_CODE_INTERNAL_ERROR;
+    // close the socket
+    close(m_client_socket);
+    m_client_socket = -1;
 
-    default:
-        return RDP_ERROR_CODE_INTERNAL_ERROR;
-    }
+    // delete the temporary file, which is used for the socket
+    unlink(m_name.c_str());
+    m_name.clear();
 }
-
diff --git a/SpiceXPI/src/plugin/controller-unix.h b/SpiceXPI/src/plugin/controller-unix.h
new file mode 100644
index 0000000..bba884d
--- /dev/null
+++ b/SpiceXPI/src/plugin/controller-unix.h
@@ -0,0 +1,97 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ *   The contents of this file are subject to the Mozilla Public License Version
+ *   1.1 (the "License"); you may not use this file except in compliance with
+ *   the License. You may obtain a copy of the License at
+ *   http://www.mozilla.org/MPL/
+ *
+ *   Software distributed under the License is distributed on an "AS IS" basis,
+ *   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ *   for the specific language governing rights and limitations under the
+ *   License.
+ *
+ *   Copyright 2009-2011, Red Hat Inc.
+ *   Copyright 2013, Red Hat Inc.
+ *   Based on mozilla.org's scriptable plugin example
+ *
+ *   The Original Code is mozilla.org code.
+ *
+ *   The Initial Developer of the Original Code is
+ *   Netscape Communications Corporation.
+ *   Portions created by the Initial Developer are Copyright (C) 1998
+ *   the Initial Developer. All Rights Reserved.
+ *
+ *   Contributor(s):
+ *   Uri Lublin
+ *   Martin Stransky
+ *   Peter Hatina
+ *   Christophe Fergeau
+ *
+ *   Alternatively, the contents of this file may be used under the terms of
+ *   either the GNU General Public License Version 2 or later (the "GPL"), or
+ *   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ *   in which case the provisions of the GPL or the LGPL are applicable instead
+ *   of those above. If you wish to allow use of your version of this file only
+ *   under the terms of either the GPL or the LGPL, and not to allow others to
+ *   use your version of this file under the terms of the MPL, indicate your
+ *   decision by deleting the provisions above and replace them with the notice
+ *   and other provisions required by the GPL or the LGPL. If you do not delete
+ *   the provisions above, a recipient may use your version of this file under
+ *   the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef SPICE_CONTROLLER_UNIX_H
+#define SPICE_CONTROLLER_UNIX_H
+
+/*
+    Basic assumption:
+    ------------------
+    Cross platform compatible.
+    Easy to transform into remote process communication
+    Secured
+
+    chosen:
+        Unix - Unix Domain Sockets (easy to change into regular sockets for remote communication)
+        Windows - Named pipe (which allows remote access and is duplex
+            (rather than anonymous pipe which is local only and one way)
+*/
+
+#include <glib.h>
+#include <glib-object.h> /* for GStrv */
+#include <gio/gio.h>
+#include <string>
+extern "C" {
+#  include <stdint.h>
+#  include <limits.h>
+}
+
+#include <spice/controller_prot.h>
+#include "controller.h"
+
+class nsPluginInstance;
+
+class SpiceControllerUnix: public SpiceController
+{
+public:
+    SpiceControllerUnix(nsPluginInstance *aPlugin);
+    virtual ~SpiceControllerUnix();
+
+    virtual void StopClient();
+    virtual uint32_t Write(const void *lpBuffer, uint32_t nBytesToWrite);
+    int Connect(int nRetries) { return SpiceController::Connect(nRetries); };
+
+private:
+    virtual int Connect();
+    virtual void Disconnect();
+    virtual void SetupControllerPipe(GStrv &env);
+    virtual bool CheckPipe();
+    virtual GStrv GetClientPath(void);
+    virtual GStrv GetFallbackClientPath(void);
+
+    int m_client_socket;
+    std::string m_tmp_dir;
+};
+
+#endif // SPICE_CONTROLLER_UNIX_H
diff --git a/SpiceXPI/src/plugin/controller.cpp b/SpiceXPI/src/plugin/controller.cpp
new file mode 100644
index 0000000..ccef1d4
--- /dev/null
+++ b/SpiceXPI/src/plugin/controller.cpp
@@ -0,0 +1,260 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ *   The contents of this file are subject to the Mozilla Public License Version
+ *   1.1 (the "License"); you may not use this file except in compliance with
+ *   the License. You may obtain a copy of the License at
+ *   http://www.mozilla.org/MPL/
+ *
+ *   Software distributed under the License is distributed on an "AS IS" basis,
+ *   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ *   for the specific language governing rights and limitations under the
+ *   License.
+ *
+ *   Copyright 2009-2011, Red Hat Inc.
+ *   Copyright 2013, Red Hat Inc.
+ *   Based on mozilla.org's scriptable plugin example
+ *
+ *   The Original Code is mozilla.org code.
+ *
+ *   The Initial Developer of the Original Code is
+ *   Netscape Communications Corporation.
+ *   Portions created by the Initial Developer are Copyright (C) 1998
+ *   the Initial Developer. All Rights Reserved.
+ *
+ *   Contributor(s):
+ *   Uri Lublin
+ *   Martin Stransky
+ *   Peter Hatina
+ *   Christophe Fergeau
+ *
+ *   Alternatively, the contents of this file may be used under the terms of
+ *   either the GNU General Public License Version 2 or later (the "GPL"), or
+ *   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ *   in which case the provisions of the GPL or the LGPL are applicable instead
+ *   of those above. If you wish to allow use of your version of this file only
+ *   under the terms of either the GPL or the LGPL, and not to allow others to
+ *   use your version of this file under the terms of the MPL, indicate your
+ *   decision by deleting the provisions above and replace them with the notice
+ *   and other provisions required by the GPL or the LGPL. If you do not delete
+ *   the provisions above, a recipient may use your version of this file under
+ *   the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "config.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <glib.h>
+
+#include "rederrorcodes.h"
+#include "controller.h"
+#include "plugin.h"
+
+SpiceController::SpiceController(nsPluginInstance *aPlugin):
+    m_pid_controller(0),
+    m_pipe(NULL),
+    m_plugin(aPlugin),
+    m_child_watch_mainloop(NULL)
+{
+}
+
+SpiceController::~SpiceController()
+{
+    g_debug(G_STRFUNC);
+    Disconnect();
+}
+
+void SpiceController::SetFilename(const std::string &name)
+{
+    m_name = name;
+}
+
+void SpiceController::SetProxy(const std::string &proxy)
+{
+    m_proxy = proxy;
+}
+
+#define FACILITY_SPICEX             50
+#define FACILITY_CREATE_RED_PROCESS 51
+#define FACILITY_STRING_OPERATION   52
+#define FACILITY_CREATE_RED_EVENT   53
+#define FACILITY_CREATE_RED_PIPE    54
+#define FACILITY_PIPE_OPERATION     55
+
+int SpiceController::Connect(const int nRetries)
+{
+    int rc = -1;
+    int sleep_time = 0;
+
+    // try to connect for specified count
+    for (int i = 0; rc != 0 && i < nRetries; ++i)
+    {
+        rc = Connect();
+        g_usleep(sleep_time * G_USEC_PER_SEC);
+        ++sleep_time;
+    }
+
+    return rc;
+}
+
+void SpiceController::Disconnect()
+{
+}
+
+void SpiceController::ChildExited(GPid pid, gint status, gpointer user_data)
+{
+    SpiceController *fake_this = (SpiceController *)user_data;
+
+    g_message("Client with pid %p exited", pid);
+
+    g_main_loop_quit(fake_this->m_child_watch_mainloop);
+    /* FIXME: we are not in the main thread!! */
+    fake_this->m_plugin->OnSpiceClientExit(status);
+}
+
+void SpiceController::WaitForPid(GPid pid)
+{
+    GMainContext *context;
+    GSource *source;
+
+    context = g_main_context_new();
+
+    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);
+
+    g_main_loop_run(m_child_watch_mainloop);
+
+    g_main_loop_unref(m_child_watch_mainloop);
+    g_main_context_unref(context);
+
+    g_spawn_close_pid(pid);
+    if (pid == m_pid_controller)
+        m_pid_controller = 0;
+}
+
+gpointer SpiceController::ClientThread(gpointer data)
+{
+    SpiceController *fake_this = (SpiceController *)data;
+    gchar **env = g_get_environ();
+    GPid pid;
+    gboolean spawned = FALSE;
+    GError *error = NULL;
+    GStrv client_argv;
+
+    // Setup client environment
+    fake_this->SetupControllerPipe(env);
+    if (!fake_this->m_proxy.empty())
+        env = g_environ_setenv(env, "SPICE_PROXY", fake_this->m_proxy.c_str(), TRUE);
+
+    // Try to spawn main client
+    client_argv = fake_this->GetClientPath();
+    if (client_argv != NULL) {
+        char *argv_str = g_strjoinv(" ", client_argv);
+        g_warning("main client cmdline: %s", argv_str);
+        g_free(argv_str);
+
+        spawned = g_spawn_async(NULL,
+                                client_argv, env,
+                                G_SPAWN_DO_NOT_REAP_CHILD,
+                                NULL, NULL, /* child_func, child_arg */
+                                &pid, &error);
+        if (error != NULL) {
+            g_warning("failed to start %s: %s", client_argv[0], error->message);
+            g_warn_if_fail(spawned == FALSE);
+            g_clear_error(&error);
+        }
+        g_strfreev(client_argv);
+    }
+
+    if (!spawned) {
+        // Fallback client for backward compatibility
+        GStrv fallback_argv;
+        char *argv_str;
+        fallback_argv = fake_this->GetFallbackClientPath();
+        if (fallback_argv == NULL) {
+            goto out;
+        }
+
+        argv_str = g_strjoinv(" ", fallback_argv);
+        g_warning("fallback client cmdline: %s", argv_str);
+        g_free(argv_str);
+
+        g_message("failed to run preferred client, running fallback client instead");
+        spawned = g_spawn_async(NULL, fallback_argv, env,
+                                G_SPAWN_DO_NOT_REAP_CHILD,
+                                NULL, NULL, /* child_func, child_arg */
+                                &pid, &error);
+        if (error != NULL) {
+            g_warning("failed to start %s: %s", fallback_argv[0], error->message);
+            g_warn_if_fail(spawned == FALSE);
+            g_clear_error(&error);
+        }
+    }
+
+    out:
+        g_strfreev(env);
+
+    if (!spawned) {
+        g_critical("ERROR failed to run spicec fallback");
+        return NULL;
+    }
+
+#ifdef XP_UNIX
+    fake_this->m_pid_controller = pid;
+#endif
+    fake_this->WaitForPid(pid);
+
+    return NULL;
+}
+
+bool SpiceController::StartClient()
+{
+    GThread *thread;
+
+    thread = g_thread_new("spice-xpi client thread", ClientThread, this);
+
+    return (thread != NULL);
+}
+
+int SpiceController::TranslateRC(int nRC)
+{
+    switch (nRC)
+    {
+    case SPICEC_ERROR_CODE_SUCCESS:
+        return 0;
+
+    case SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED:
+        return RDP_ERROR_CODE_HOST_NOT_FOUND;
+
+    case SPICEC_ERROR_CODE_CONNECT_FAILED:
+        return RDP_ERROR_CODE_WINSOCK_CONNECT_FAILED;
+
+    case SPICEC_ERROR_CODE_ERROR:
+    case SPICEC_ERROR_CODE_SOCKET_FAILED:
+        return RDP_ERROR_CODE_INTERNAL_ERROR;
+
+    case SPICEC_ERROR_CODE_RECV_FAILED:
+        return RDP_ERROR_RECV_WINSOCK_FAILED;
+
+    case SPICEC_ERROR_CODE_SEND_FAILED:
+        return RDP_ERROR_SEND_WINSOCK_FAILED;
+
+    case SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY:
+        return RDP_ERROR_CODE_OUT_OF_MEMORY;
+
+    case SPICEC_ERROR_CODE_AGENT_TIMEOUT:
+        return RDP_ERROR_CODE_TIMEOUT;
+
+    case SPICEC_ERROR_CODE_AGENT_ERROR:
+        return RDP_ERROR_CODE_INTERNAL_ERROR;
+
+    default:
+        return RDP_ERROR_CODE_INTERNAL_ERROR;
+    }
+}
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index b38f7bc..02a4302 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -59,6 +59,7 @@
 
 #include <glib.h>
 #include <glib-object.h> /* for GStrv */
+#include <gio/gio.h>
 #include <string>
 extern "C" {
 #  include <stdint.h>
@@ -73,33 +74,35 @@ class SpiceController
 {
 public:
     SpiceController(nsPluginInstance *aPlugin);
-    ~SpiceController();
+    virtual ~SpiceController();
 
     bool StartClient();
-    void StopClient();
+    virtual void StopClient() = 0;
     void SetFilename(const std::string &name);
     void SetProxy(const std::string &proxy);
     int Connect(int nRetries);
-    void Disconnect();
-    uint32_t Write(const void *lpBuffer, uint32_t nBytesToWrite);
+    virtual void Disconnect();
+    virtual uint32_t Write(const void *lpBuffer, uint32_t nBytesToWrite) = 0;
 
     static int TranslateRC(int nRC);
 
+protected:
+    std::string m_name;
+    std::string m_proxy;
+    GPid m_pid_controller;
+    GOutputStream *m_pipe;
+
 private:
-    int Connect();
+    virtual int Connect() = 0;
     void WaitForPid(GPid pid);
-    void SetupControllerPipe(GStrv &env);
-    GStrv GetClientPath(void);
-    GStrv GetFallbackClientPath(void);
+    virtual void SetupControllerPipe(GStrv &env) = 0;
+    virtual bool CheckPipe() = 0;
+    virtual GStrv GetClientPath(void) = 0;
+    virtual GStrv GetFallbackClientPath(void) = 0;
     static void ChildExited(GPid pid, gint status, gpointer user_data);
     static gpointer ClientThread(gpointer data);
 
     nsPluginInstance *m_plugin;
-    int m_client_socket;
-    std::string m_name;
-    std::string m_tmp_dir;
-    pid_t m_pid_controller;
-    std::string m_proxy;
 
     GMainLoop *m_child_watch_mainloop;
 };
diff --git a/SpiceXPI/src/plugin/plugin.cpp b/SpiceXPI/src/plugin/plugin.cpp
index 6531523..cd17620 100644
--- a/SpiceXPI/src/plugin/plugin.cpp
+++ b/SpiceXPI/src/plugin/plugin.cpp
@@ -66,7 +66,7 @@ extern "C" {
 #include <fstream>
 #include <set>
 
-#include "controller.h"
+#include "controller-unix.h"
 #include "plugin.h"
 #include "nsScriptablePeer.h"
 
@@ -172,7 +172,6 @@ void NS_DestroyPluginInstance(nsPluginInstanceBase *aPlugin)
 nsPluginInstance::nsPluginInstance(NPP aInstance):
     nsPluginInstanceBase(),
     m_connected_status(-2),
-    m_external_controller(this),
     m_instance(aInstance),
     m_initialized(true),
     m_window(NULL),
@@ -187,6 +186,8 @@ nsPluginInstance::nsPluginInstance(NPP aInstance):
 #if !GLIB_CHECK_VERSION(2, 35, 0)
     g_type_init();
 #endif
+
+    m_external_controller = new SpiceControllerUnix(this);
 }
 
 nsPluginInstance::~nsPluginInstance()
@@ -197,6 +198,7 @@ nsPluginInstance::~nsPluginInstance()
     // and zero its m_plugin member
     if (m_scriptable_peer)
         NPN_ReleaseObject(m_scriptable_peer);
+    delete(m_external_controller);
 }
 
 NPBool nsPluginInstance::init(NPWindow *aWindow)
@@ -222,7 +224,7 @@ NPBool nsPluginInstance::init(NPWindow *aWindow)
     m_color_depth.clear();
     m_disable_effects.clear();
     m_proxy.clear();
-    m_external_controller.SetProxy(std::string());
+    m_external_controller->SetProxy(std::string());
 
     m_fullscreen = false;
     m_smartcard = false;
@@ -527,12 +529,12 @@ char *nsPluginInstance::GetProxy() const
 void nsPluginInstance::SetProxy(const char *aProxy)
 {
     m_proxy = aProxy;
-    m_external_controller.SetProxy(m_proxy);
+    m_external_controller->SetProxy(m_proxy);
 }
 
 void nsPluginInstance::WriteToPipe(const void *data, uint32_t size)
 {
-    m_external_controller.Write(data, size);
+    m_external_controller->Write(data, size);
 }
 
 void nsPluginInstance::SendInit()
@@ -629,12 +631,12 @@ void nsPluginInstance::Connect()
         return;
     }
 
-    if (!m_external_controller.StartClient()) {
+    if (!m_external_controller->StartClient()) {
         g_critical("failed to start SPICE client");
         return;
     }
 
-    if (m_external_controller.Connect(10) != 0)
+    if (m_external_controller->Connect(10) != 0)
     {
         g_critical("could not connect to spice client controller");
         return;
@@ -682,7 +684,7 @@ void nsPluginInstance::Show()
 
 void nsPluginInstance::Disconnect()
 {
-    m_external_controller.StopClient();
+    m_external_controller->StopClient();
 }
 
 void nsPluginInstance::ConnectedStatus(int32_t *retval)
@@ -756,11 +758,11 @@ void nsPluginInstance::CallOnDisconnected(int code)
 
 void nsPluginInstance::OnSpiceClientExit(int exit_code)
 {
-    m_connected_status = m_external_controller.TranslateRC(exit_code);
+    m_connected_status = m_external_controller->TranslateRC(exit_code);
     if (!getenv("SPICE_XPI_DEBUG"))
     {
         CallOnDisconnected(exit_code);
-        m_external_controller.Disconnect();
+        m_external_controller->Disconnect();
     }
 
     RemoveTrustStoreFile();
diff --git a/SpiceXPI/src/plugin/plugin.h b/SpiceXPI/src/plugin/plugin.h
index 5e7f079..7884dc7 100644
--- a/SpiceXPI/src/plugin/plugin.h
+++ b/SpiceXPI/src/plugin/plugin.h
@@ -190,7 +190,7 @@ private:
     bool RemoveTrustStoreFile();
 
     int32_t m_connected_status;
-    SpiceController m_external_controller;
+    SpiceController *m_external_controller;
 
     NPP m_instance;
     NPBool m_initialized;
commit 5e5ab9cb8b1bd22c18ce3894686dba5e2df62839
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Wed Feb 27 16:56:33 2013 +0100

    Use g_usleep in SpiceController::connect(int retries)
    
    g_usleep is more portable than sleep()

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 16f7033..04257e9 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -131,7 +131,7 @@ int SpiceController::Connect(const int nRetries)
     for (int i = 0; rc != 0 && i < nRetries; ++i)
     {
         rc = Connect();
-        sleep(sleep_time);
+        g_usleep(sleep_time * G_USEC_PER_SEC);
         ++sleep_time;
     }
 
commit 4bc345834300a729f3c50c46c09a0e8c554f4517
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Feb 12 18:48:30 2013 +0100

    Add helper function to get client paths
    
    The SPICE client paths will be different on Windows and Unix so add
    a helper function returning the paths to use to spawn the clients.

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 338f1a8..16f7033 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -138,6 +138,20 @@ int SpiceController::Connect(const int nRetries)
     return rc;
 }
 
+GStrv SpiceControllerUnix::GetClientPath()
+{
+    const char *client_argv[] = { "/usr/libexec/spice-xpi-client", NULL };
+
+    return g_strdupv((GStrv)client_argv);
+}
+
+GStrv SpiceControllerUnix::GetFallbackClientPath()
+{
+    const char *fallback_argv[] = { "/usr/bin/spicec", "--controller", NULL };
+
+    return g_strdupv((GStrv)fallback_argv);
+}
+
 void SpiceController::SetupControllerPipe(GStrv &env)
 {
     std::string socket_file(this->m_tmp_dir);
@@ -208,42 +222,66 @@ void SpiceController::WaitForPid(GPid pid)
 
 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;
+    gboolean spawned = FALSE;
     GError *error = NULL;
+    GStrv client_argv;
 
-
+    // Setup client environment
     fake_this->SetupControllerPipe(env);
-
     if (!fake_this->m_proxy.empty())
         env = g_environ_setenv(env, "SPICE_PROXY", fake_this->m_proxy.c_str(), 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);
+    // Try to spawn main client
+    client_argv = fake_this->GetClientPath();
+    if (client_argv != NULL) {
+        char *argv_str = g_strjoinv(" ", client_argv);
+        g_warning("main client cmdline: %s", argv_str);
+        g_free(argv_str);
+
+        spawned = g_spawn_async(NULL,
+                                client_argv, env,
+                                G_SPAWN_DO_NOT_REAP_CHILD,
+                                NULL, NULL, /* child_func, child_arg */
+                                &pid, &error);
+        if (error != NULL) {
+            g_warning("failed to start %s: %s", client_argv[0], error->message);
+            g_warn_if_fail(spawned == FALSE);
+            g_clear_error(&error);
+        }
+        g_strfreev(client_argv);
     }
+
     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,
+        // Fallback client for backward compatibility
+        GStrv fallback_argv;
+        char *argv_str;
+        fallback_argv = fake_this->GetFallbackClientPath();
+        if (fallback_argv == NULL) {
+            goto out;
+        }
+
+        argv_str = g_strjoinv(" ", fallback_argv);
+        g_warning("fallback client cmdline: %s", argv_str);
+        g_free(argv_str);
+
+        g_message("failed to run preferred client, running fallback client instead");
+        spawned = g_spawn_async(NULL, fallback_argv, env,
                                 G_SPAWN_DO_NOT_REAP_CHILD,
                                 NULL, NULL, /* child_func, child_arg */
                                 &pid, &error);
+        if (error != NULL) {
+            g_warning("failed to start %s: %s", fallback_argv[0], error->message);
+            g_warn_if_fail(spawned == FALSE);
+            g_clear_error(&error);
+        }
     }
-    if (error != NULL) {
-        g_warning("failed to start spice-xpi-client: %s", error->message);
-        g_clear_error(&error);
-    }
+
+out:
     g_strfreev(env);
+
     if (!spawned) {
         g_critical("ERROR failed to run spicec fallback");
         return NULL;
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index e19187c..b38f7bc 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -89,6 +89,8 @@ private:
     int Connect();
     void WaitForPid(GPid pid);
     void SetupControllerPipe(GStrv &env);
+    GStrv GetClientPath(void);
+    GStrv GetFallbackClientPath(void);
     static void ChildExited(GPid pid, gint status, gpointer user_data);
     static gpointer ClientThread(gpointer data);
 
commit b1d5ebb329a2c17262bbfd6bb00fbbf01f9faf5b
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Wed Feb 13 14:28:03 2013 +0100

    Add SpiceController::SetupControllerPipe
    
    Communication with the client is done differently on Windows and
    Linux (named pipe VS unix socket), so move this platform-specific
    setup to a distinct method.

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index ed4e174..338f1a8 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -138,6 +138,16 @@ int SpiceController::Connect(const int nRetries)
     return rc;
 }
 
+void SpiceController::SetupControllerPipe(GStrv &env)
+{
+    std::string socket_file(this->m_tmp_dir);
+    socket_file += "/spice-xpi";
+
+    this->SetFilename(socket_file);
+
+    env = g_environ_setenv(env, "SPICE_XPI_SOCKET", socket_file.c_str(), TRUE);
+}
+
 void SpiceController::Disconnect()
 {
     // close the socket
@@ -205,12 +215,9 @@ gpointer SpiceController::ClientThread(gpointer data)
     gboolean spawned;
     GError *error = NULL;
 
-    std::string socket_file(fake_this->m_tmp_dir);
-    socket_file += "/spice-xpi";
 
-    fake_this->SetFilename(socket_file);
+    fake_this->SetupControllerPipe(env);
 
-    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);
 
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index e63e121..e19187c 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -58,6 +58,7 @@
 */
 
 #include <glib.h>
+#include <glib-object.h> /* for GStrv */
 #include <string>
 extern "C" {
 #  include <stdint.h>
@@ -87,6 +88,7 @@ public:
 private:
     int Connect();
     void WaitForPid(GPid pid);
+    void SetupControllerPipe(GStrv &env);
     static void ChildExited(GPid pid, gint status, gpointer user_data);
     static gpointer ClientThread(gpointer data);
 
commit e7c043bbe49858e01dc95df1aa2583519ac51580
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 16:49:20 2013 +0100

    Use glib functions to spawn/watch client
    
    This removes quite a lot of OS-specific code.

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 489136e..ed4e174 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -162,94 +162,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_new("spice-xpi client thread", ClientThread, this);
 
-    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 d7d3875..e63e121 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -57,6 +57,7 @@
             (rather than anonymous pipe which is local only and one way)
 */
 
+#include <glib.h>
 #include <string>
 extern "C" {
 #  include <stdint.h>
@@ -85,7 +86,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;
@@ -93,6 +96,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)
 
commit 271833906304670e557d45b929c458c1e890b036
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 14:48:15 2013 +0100

    Move StartClient() to the controller class
    
    This is platform specific, and is related to starting the
    receiver of controller messages, so let's move it with the rest
    of the platform-specific code.

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 0d8f0f8..489136e 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -53,20 +53,29 @@ extern "C" {
 #  include <fcntl.h>
 #  include <sys/socket.h>
 #  include <sys/un.h>
+#  include <sys/wait.h>
 }
 
 #include "rederrorcodes.h"
 #include "controller.h"
+#include "plugin.h"
 
-SpiceController::SpiceController():
+SpiceController::SpiceController(nsPluginInstance *aPlugin):
+    m_plugin(aPlugin),
     m_client_socket(-1)
 {
+    // create temporary directory in /tmp
+    char tmp_dir[] = "/tmp/spicec-XXXXXX";
+    m_tmp_dir = mkdtemp(tmp_dir);
 }
 
 SpiceController::~SpiceController()
 {
     g_debug(G_STRFUNC);
     Disconnect();
+
+    // delete the temporary directory used for a client socket
+    rmdir(m_tmp_dir.c_str());
 }
 
 void SpiceController::SetFilename(const std::string &name)
@@ -74,6 +83,11 @@ void SpiceController::SetFilename(const std::string &name)
     m_name = name;
 }
 
+void SpiceController::SetProxy(const std::string &proxy)
+{
+    m_proxy = proxy;
+}
+
 int SpiceController::Connect()
 {
     // check, if we have a filename for socket to create
@@ -148,6 +162,96 @@ uint32_t SpiceController::Write(const void *lpBuffer, uint32_t nBytesToWrite)
     return len;
 }
 
+bool SpiceController::StartClient()
+{
+    std::string socket_file(m_tmp_dir);
+    socket_file += "/spice-xpi";
+
+    /* 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();
+
+        close(pipe_fds[1]);
+        pipe_fds[1] = -1;
+
+        char c;
+        if (read(pipe_fds[0], &c, 1) != 0)
+            g_critical("Error while reading on pipe: %s", g_strerror(errno));
+
+        close(pipe_fds[0]);
+        pipe_fds[0] = -1;
+
+        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);
+
+        execle("/usr/libexec/spice-xpi-client",
+               "/usr/libexec/spice-xpi-client", NULL,
+               env);
+        g_message("failed to run spice-xpi-client, running spicec instead");
+
+        // TODO: temporary fallback for backward compatibility
+        execle("/usr/bin/spicec",
+               "/usr/bin/spicec", "--controller", NULL,
+               env);
+
+        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);
+
+        close(pipe_fds[0]);
+        pipe_fds[0] = -1;
+
+        pthread_t controller_thread_id;
+        pthread_create(&controller_thread_id, NULL, ControllerWaitHelper,
+            reinterpret_cast<void*>(this));
+
+        close(pipe_fds[1]);
+        pipe_fds[1] = -1;
+
+        this->SetFilename(socket_file);
+
+        return true;
+    }
+
+    g_return_val_if_reached(false);
+}
+
+void SpiceController::StopClient()
+{
+    if (m_pid_controller > 0)
+        kill(-m_pid_controller, SIGTERM);
+}
+
+void *SpiceController::ControllerWaitHelper(void *opaque)
+{
+    SpiceController *fake_this = reinterpret_cast<SpiceController *>(opaque);
+    if (!fake_this)
+        return NULL;
+
+    int exit_code;
+    waitpid(fake_this->m_pid_controller, &exit_code, 0);
+    g_debug("child finished, pid: %"G_GUINT64_FORMAT, (guint64)exit_code);
+
+    fake_this->m_plugin->OnSpiceClientExit(exit_code);
+    fake_this->m_pid_controller = -1;
+
+    return NULL;
+}
+
 int SpiceController::TranslateRC(int nRC)
 {
     switch (nRC)
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index 001d2b3..d7d3875 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -65,13 +65,18 @@ extern "C" {
 
 #include <spice/controller_prot.h>
 
+class nsPluginInstance;
+
 class SpiceController
 {
 public:
-    SpiceController();
+    SpiceController(nsPluginInstance *aPlugin);
     ~SpiceController();
 
+    bool StartClient();
+    void StopClient();
     void SetFilename(const std::string &name);
+    void SetProxy(const std::string &proxy);
     int Connect(int nRetries);
     void Disconnect();
     uint32_t Write(const void *lpBuffer, uint32_t nBytesToWrite);
@@ -80,8 +85,14 @@ public:
 
 private:
     int Connect();
+    static void *ControllerWaitHelper(void *opaque);
+
+    nsPluginInstance *m_plugin;
     int m_client_socket;
     std::string m_name;
+    std::string m_tmp_dir;
+    pid_t m_pid_controller;
+    std::string m_proxy;
 };
 
 #endif // SPICE_CONTROLLER_H
diff --git a/SpiceXPI/src/plugin/plugin.cpp b/SpiceXPI/src/plugin/plugin.cpp
index ba5747e..6531523 100644
--- a/SpiceXPI/src/plugin/plugin.cpp
+++ b/SpiceXPI/src/plugin/plugin.cpp
@@ -47,10 +47,6 @@
 
 #include "config.h"
 
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
@@ -175,8 +171,8 @@ void NS_DestroyPluginInstance(nsPluginInstanceBase *aPlugin)
 
 nsPluginInstance::nsPluginInstance(NPP aInstance):
     nsPluginInstanceBase(),
-    m_pid_controller(-1),
     m_connected_status(-2),
+    m_external_controller(this),
     m_instance(aInstance),
     m_initialized(true),
     m_window(NULL),
@@ -188,10 +184,6 @@ nsPluginInstance::nsPluginInstance(NPP aInstance):
     m_usb_auto_share(true),
     m_scriptable_peer(NULL)
 {
-    // create temporary directory in /tmp
-    char tmp_dir[] = "/tmp/spicec-XXXXXX";
-    m_tmp_dir = mkdtemp(tmp_dir);
-
 #if !GLIB_CHECK_VERSION(2, 35, 0)
     g_type_init();
 #endif
@@ -205,9 +197,6 @@ nsPluginInstance::~nsPluginInstance()
     // and zero its m_plugin member
     if (m_scriptable_peer)
         NPN_ReleaseObject(m_scriptable_peer);
-
-    // delete the temporary directory used for a client socket
-    rmdir(m_tmp_dir.c_str());
 }
 
 NPBool nsPluginInstance::init(NPWindow *aWindow)
@@ -233,6 +222,7 @@ NPBool nsPluginInstance::init(NPWindow *aWindow)
     m_color_depth.clear();
     m_disable_effects.clear();
     m_proxy.clear();
+    m_external_controller.SetProxy(std::string());
 
     m_fullscreen = false;
     m_smartcard = false;
@@ -537,6 +527,7 @@ char *nsPluginInstance::GetProxy() const
 void nsPluginInstance::SetProxy(const char *aProxy)
 {
     m_proxy = aProxy;
+    m_external_controller.SetProxy(m_proxy);
 }
 
 void nsPluginInstance::WriteToPipe(const void *data, uint32_t size)
@@ -586,74 +577,6 @@ void nsPluginInstance::SendStr(uint32_t id, std::string str)
     free(msg);
 }
 
-bool nsPluginInstance::StartClient()
-{
-    std::string socket_file(m_tmp_dir);
-    socket_file += "/spice-xpi";
-
-    /* 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();
-
-        close(pipe_fds[1]);
-        pipe_fds[1] = -1;
-
-        char c;
-        if (read(pipe_fds[0], &c, 1) != 0)
-            g_critical("Error while reading on pipe: %s", g_strerror(errno));
-
-        close(pipe_fds[0]);
-        pipe_fds[0] = -1;
-
-        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);
-
-        execle("/usr/libexec/spice-xpi-client",
-               "/usr/libexec/spice-xpi-client", NULL,
-               env);
-        g_message("failed to run spice-xpi-client, running spicec instead");
-
-        // TODO: temporary fallback for backward compatibility
-        execle("/usr/bin/spicec",
-               "/usr/bin/spicec", "--controller", NULL,
-               env);
-
-        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);
-
-        close(pipe_fds[0]);
-        pipe_fds[0] = -1;
-
-        pthread_t controller_thread_id;
-        pthread_create(&controller_thread_id, NULL, ControllerWaitHelper,
-            reinterpret_cast<void*>(this));
-
-        close(pipe_fds[1]);
-        pipe_fds[1] = -1;
-
-        m_external_controller.SetFilename(socket_file);
-
-        return true;
-    }
-
-    g_return_val_if_reached(false);
-}
-
 bool nsPluginInstance::CreateTrustStoreFile(const std::string &trust_store)
 {
     GFile *tmp_file;
@@ -706,7 +629,7 @@ void nsPluginInstance::Connect()
         return;
     }
 
-    if (!this->StartClient()) {
+    if (!m_external_controller.StartClient()) {
         g_critical("failed to start SPICE client");
         return;
     }
@@ -759,8 +682,7 @@ void nsPluginInstance::Show()
 
 void nsPluginInstance::Disconnect()
 {
-    if (m_pid_controller > 0)
-        kill(-m_pid_controller, SIGTERM);
+    m_external_controller.StopClient();
 }
 
 void nsPluginInstance::ConnectedStatus(int32_t *retval)
@@ -832,26 +754,16 @@ void nsPluginInstance::CallOnDisconnected(int code)
     NPN_ReleaseVariantValue(&var_on_disconnected);
 }
 
-void *nsPluginInstance::ControllerWaitHelper(void *opaque)
+void nsPluginInstance::OnSpiceClientExit(int exit_code)
 {
-    nsPluginInstance *fake_this = reinterpret_cast<nsPluginInstance *>(opaque);
-    if (!fake_this)
-        return NULL;
-
-    int exit_code;
-    waitpid(fake_this->m_pid_controller, &exit_code, 0);
-    g_debug("child finished, pid: %"G_GUINT64_FORMAT, (guint64)exit_code);
-
-    fake_this->m_connected_status = fake_this->m_external_controller.TranslateRC(exit_code);
+    m_connected_status = m_external_controller.TranslateRC(exit_code);
     if (!getenv("SPICE_XPI_DEBUG"))
     {
-        fake_this->CallOnDisconnected(exit_code);
-        fake_this->m_external_controller.Disconnect();
+        CallOnDisconnected(exit_code);
+        m_external_controller.Disconnect();
     }
-    
-    fake_this->RemoveTrustStoreFile();
-    fake_this->m_pid_controller = -1;
-    return NULL;
+
+    RemoveTrustStoreFile();
 }
 
 // ==============================
diff --git a/SpiceXPI/src/plugin/plugin.h b/SpiceXPI/src/plugin/plugin.h
index ea50ca5..5e7f079 100644
--- a/SpiceXPI/src/plugin/plugin.h
+++ b/SpiceXPI/src/plugin/plugin.h
@@ -174,8 +174,9 @@ public:
 
     NPObject *GetScriptablePeer();
     
+    void OnSpiceClientExit(int exit_code);
+
 private:
-    static void *ControllerWaitHelper(void *opaque);
     void WriteToPipe(const void *data, uint32_t size);
     void SendInit();
     void SendMsg(uint32_t id);
@@ -185,12 +186,9 @@ private:
     void CallOnDisconnected(int code);
   
 private:
-    bool StartClient();
-    bool CreateTrustStore();
     bool CreateTrustStoreFile(const std::string &trust_store);
     bool RemoveTrustStoreFile();
 
-    pid_t m_pid_controller;
     int32_t m_connected_status;
     SpiceController m_external_controller;
 
@@ -224,7 +222,6 @@ private:
     std::string m_proxy;
     
     NPObject *m_scriptable_peer;
-    std::string m_tmp_dir;
     std::string m_trust_store_file;
 };
 
commit 473c1d5b2aeb350ef51d8e0b87bd56a76ea122e9
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 13:02:53 2013 +0100

    Mark Controller::Connect() as private
    
    It's only used internally, external code calls Connect(int retries)

diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index dbcc424..001d2b3 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -72,7 +72,6 @@ public:
     ~SpiceController();
 
     void SetFilename(const std::string &name);
-    int Connect();
     int Connect(int nRetries);
     void Disconnect();
     uint32_t Write(const void *lpBuffer, uint32_t nBytesToWrite);
@@ -80,6 +79,7 @@ public:
     static int TranslateRC(int nRC);
 
 private:
+    int Connect();
     int m_client_socket;
     std::string m_name;
 };
commit 50281544226f88160dbfc299c3a0c6a0c6524d0a
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 13:02:28 2013 +0100

    Remove unused controller constructor

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index 2499cae..0d8f0f8 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -63,12 +63,6 @@ SpiceController::SpiceController():
 {
 }
 
-SpiceController::SpiceController(const std::string &name):
-    m_client_socket(-1),
-    m_name(name)
-{
-}
-
 SpiceController::~SpiceController()
 {
     g_debug(G_STRFUNC);
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index af455a2..dbcc424 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -69,7 +69,6 @@ class SpiceController
 {
 public:
     SpiceController();
-    SpiceController(const std::string &name);
     ~SpiceController();
 
     void SetFilename(const std::string &name);
commit 39906d7c340c187a025a31ad74e043d5a7b1a847
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Wed Feb 13 14:51:00 2013 +0100

    Update copyright notices

diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
index b912c27..2499cae 100644
--- a/SpiceXPI/src/plugin/controller-unix.cpp
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -11,7 +11,7 @@
  *   for the specific language governing rights and limitations under the
  *   License.
  *
- *   Copyright 2009-2011, Red Hat Inc.
+ *   Copyright 2009-2013, Red Hat Inc.
  *   Based on mozilla.org's scriptable plugin example
  *
  *   The Original Code is mozilla.org code.
@@ -25,6 +25,7 @@
  *   Uri Lublin
  *   Martin Stransky
  *   Peter Hatina
+ *   Christophe Fergeau
  *
  *   Alternatively, the contents of this file may be used under the terms of
  *   either the GNU General Public License Version 2 or later (the "GPL"), or
diff --git a/SpiceXPI/src/plugin/controller.h b/SpiceXPI/src/plugin/controller.h
index 07c04c7..af455a2 100644
--- a/SpiceXPI/src/plugin/controller.h
+++ b/SpiceXPI/src/plugin/controller.h
@@ -11,7 +11,7 @@
  *   for the specific language governing rights and limitations under the
  *   License.
  *
- *   Copyright 2009-2011, Red Hat Inc.
+ *   Copyright 2009-2013, Red Hat Inc.
  *   Based on mozilla.org's scriptable plugin example
  *
  *   The Original Code is mozilla.org code.
@@ -25,6 +25,7 @@
  *   Uri Lublin
  *   Martin Stransky
  *   Peter Hatina
+ *   Christophe Fergeau
  *
  *   Alternatively, the contents of this file may be used under the terms of
  *   either the GNU General Public License Version 2 or later (the "GPL"), or
diff --git a/SpiceXPI/src/plugin/plugin.cpp b/SpiceXPI/src/plugin/plugin.cpp
index 51d1c09..ba5747e 100644
--- a/SpiceXPI/src/plugin/plugin.cpp
+++ b/SpiceXPI/src/plugin/plugin.cpp
@@ -11,7 +11,7 @@
  *   for the specific language governing rights and limitations under the
  *   License.
  *
- *   Copyright 2009-2011, Red Hat Inc.
+ *   Copyright 2009-2013, Red Hat Inc.
  *   Based on mozilla.org's scriptable plugin example
  *
  *   The Original Code is mozilla.org code.
@@ -25,6 +25,7 @@
  *   Uri Lublin
  *   Martin Stransky
  *   Peter Hatina
+ *   Christophe Fergeau
  *
  *   Alternatively, the contents of this file may be used under the terms of
  *   either the GNU General Public License Version 2 or later (the "GPL"), or
commit 5157ea84e75c25eb36d7b4531474f8786ac69c6b
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 12:01:43 2013 +0100

    Rename controller.cpp to controller-unix.cpp
    
    It mostly contain low-level controller/plugin communication code,
    and this is mainly non-portable code, let's rename this file to
    make this explicit. We will then add a controller.cpp file
    containing the generic code, and a controller-win.cpp file
    with the Windows specific code.

diff --git a/SpiceXPI/src/plugin/Makefile.am b/SpiceXPI/src/plugin/Makefile.am
index d5434e5..2f12e70 100644
--- a/SpiceXPI/src/plugin/Makefile.am
+++ b/SpiceXPI/src/plugin/Makefile.am
@@ -26,8 +26,8 @@ libnsISpicec_la_SOURCES =			\
 	$(top_srcdir)/common/rederrorcodes.h	\
 	glib-compat.c				\
 	glib-compat.h				\
-	controller.cpp				\
 	controller.h				\
+	controller-unix.cpp			\
 	npapi/npapi.h				\
 	npapi/npfunctions.h			\
 	npapi/npruntime.h			\
diff --git a/SpiceXPI/src/plugin/controller-unix.cpp b/SpiceXPI/src/plugin/controller-unix.cpp
new file mode 100644
index 0000000..b912c27
--- /dev/null
+++ b/SpiceXPI/src/plugin/controller-unix.cpp
@@ -0,0 +1,192 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ *   The contents of this file are subject to the Mozilla Public License Version
+ *   1.1 (the "License"); you may not use this file except in compliance with
+ *   the License. You may obtain a copy of the License at
+ *   http://www.mozilla.org/MPL/
+ *
+ *   Software distributed under the License is distributed on an "AS IS" basis,
+ *   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ *   for the specific language governing rights and limitations under the
+ *   License.
+ *
+ *   Copyright 2009-2011, Red Hat Inc.
+ *   Based on mozilla.org's scriptable plugin example
+ *
+ *   The Original Code is mozilla.org code.
+ *
+ *   The Initial Developer of the Original Code is
+ *   Netscape Communications Corporation.
+ *   Portions created by the Initial Developer are Copyright (C) 1998
+ *   the Initial Developer. All Rights Reserved.
+ *
+ *   Contributor(s):
+ *   Uri Lublin
+ *   Martin Stransky
+ *   Peter Hatina
+ *
+ *   Alternatively, the contents of this file may be used under the terms of
+ *   either the GNU General Public License Version 2 or later (the "GPL"), or
+ *   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ *   in which case the provisions of the GPL or the LGPL are applicable instead
+ *   of those above. If you wish to allow use of your version of this file only
+ *   under the terms of either the GPL or the LGPL, and not to allow others to
+ *   use your version of this file under the terms of the MPL, indicate your
+ *   decision by deleting the provisions above and replace them with the notice
+ *   and other provisions required by the GPL or the LGPL. If you do not delete
+ *   the provisions above, a recipient may use your version of this file under
+ *   the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <glib.h>
+
+extern "C" {
+#  include <stdint.h>
+#  include <unistd.h>
+#  include <fcntl.h>
+#  include <sys/socket.h>
+#  include <sys/un.h>
+}
+
+#include "rederrorcodes.h"
+#include "controller.h"
+
+SpiceController::SpiceController():
+    m_client_socket(-1)
+{
+}
+
+SpiceController::SpiceController(const std::string &name):
+    m_client_socket(-1),
+    m_name(name)
+{
+}
+
+SpiceController::~SpiceController()
+{
+    g_debug(G_STRFUNC);
+    Disconnect();
+}
+
+void SpiceController::SetFilename(const std::string &name)
+{
+    m_name = name;
+}
+
+int SpiceController::Connect()
+{
+    // check, if we have a filename for socket to create
+    if (m_name.empty())
+        return -1;
+
+    if (m_client_socket == -1)
+    {
+        if ((m_client_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+        {
+            g_critical("controller socket: %s", g_strerror(errno));
+            return -1;
+        }
+    }
+
+    struct sockaddr_un remote;
+    remote.sun_family = AF_UNIX;
+    if (m_name.length() + 1 > sizeof(remote.sun_path))
+        return -1;
+    strcpy(remote.sun_path, m_name.c_str());
+
+    int rc = connect(m_client_socket, (struct sockaddr *) &remote, strlen(remote.sun_path) + sizeof(remote.sun_family));
+    if (rc == -1)
+    {
+        g_critical("controller connect: %s", g_strerror(errno));
+    }
+    else
+    {
+        g_debug("controller connected");
+    }
+
+    return rc;
+}
+
+int SpiceController::Connect(const int nRetries)
+{
+    int rc = -1;
+    int sleep_time = 0;
+
+    // try to connect for specified count
+    for (int i = 0; rc != 0 && i < nRetries; ++i)
+    {
+        rc = Connect();
+        sleep(sleep_time);
+        ++sleep_time;
+    }
+
+    return rc;
+}
+
+void SpiceController::Disconnect()
+{
+    // close the socket
+    close(m_client_socket);
+    m_client_socket = -1;
+
+    // delete the temporary file, which is used for the socket
+    unlink(m_name.c_str());
+    m_name.clear();
+}
+
+uint32_t SpiceController::Write(const void *lpBuffer, uint32_t nBytesToWrite)
+{
+    ssize_t len = send(m_client_socket, lpBuffer, nBytesToWrite, 0);
+
+    if (len != (ssize_t)nBytesToWrite)
+    {
+        g_warning("incomplete send, bytes to write = %u, bytes written = %zd: %s",
+                  nBytesToWrite, len, g_strerror(errno));
+    }
+
+    return len;
+}
+
+int SpiceController::TranslateRC(int nRC)
+{
+    switch (nRC)
+    {
+    case SPICEC_ERROR_CODE_SUCCESS:
+        return 0;
+
+    case SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED:
+        return RDP_ERROR_CODE_HOST_NOT_FOUND;
+
+    case SPICEC_ERROR_CODE_CONNECT_FAILED:
+        return RDP_ERROR_CODE_WINSOCK_CONNECT_FAILED;
+
+    case SPICEC_ERROR_CODE_ERROR:
+    case SPICEC_ERROR_CODE_SOCKET_FAILED:
+        return RDP_ERROR_CODE_INTERNAL_ERROR;
+
+    case SPICEC_ERROR_CODE_RECV_FAILED:
+        return RDP_ERROR_RECV_WINSOCK_FAILED;
+
+    case SPICEC_ERROR_CODE_SEND_FAILED:
+        return RDP_ERROR_SEND_WINSOCK_FAILED;
+
+    case SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY:
+        return RDP_ERROR_CODE_OUT_OF_MEMORY;
+
+    case SPICEC_ERROR_CODE_AGENT_TIMEOUT:
+        return RDP_ERROR_CODE_TIMEOUT;
+
+    case SPICEC_ERROR_CODE_AGENT_ERROR:
+        return RDP_ERROR_CODE_INTERNAL_ERROR;
+
+    default:
+        return RDP_ERROR_CODE_INTERNAL_ERROR;
+    }
+}
+
diff --git a/SpiceXPI/src/plugin/controller.cpp b/SpiceXPI/src/plugin/controller.cpp
deleted file mode 100644
index 99e58b3..0000000
--- a/SpiceXPI/src/plugin/controller.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- *   The contents of this file are subject to the Mozilla Public License Version
- *   1.1 (the "License"); you may not use this file except in compliance with
- *   the License. You may obtain a copy of the License at
- *   http://www.mozilla.org/MPL/
- *
- *   Software distributed under the License is distributed on an "AS IS" basis,
- *   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- *   for the specific language governing rights and limitations under the
- *   License.
- *
- *   Copyright 2009-2011, Red Hat Inc.
- *   Based on mozilla.org's scriptable plugin example
- *
- *   The Original Code is mozilla.org code.
- *
- *   The Initial Developer of the Original Code is
- *   Netscape Communications Corporation.
- *   Portions created by the Initial Developer are Copyright (C) 1998
- *   the Initial Developer. All Rights Reserved.
- *
- *   Contributor(s):
- *   Uri Lublin
- *   Martin Stransky
- *   Peter Hatina
- *
- *   Alternatively, the contents of this file may be used under the terms of
- *   either the GNU General Public License Version 2 or later (the "GPL"), or
- *   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- *   in which case the provisions of the GPL or the LGPL are applicable instead
- *   of those above. If you wish to allow use of your version of this file only
- *   under the terms of either the GPL or the LGPL, and not to allow others to
- *   use your version of this file under the terms of the MPL, indicate your
- *   decision by deleting the provisions above and replace them with the notice
- *   and other provisions required by the GPL or the LGPL. If you do not delete
- *   the provisions above, a recipient may use your version of this file under
- *   the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "config.h"
-
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <cerrno>
-#include <glib.h>
-
-extern "C" {
-#  include <stdint.h>
-#  include <unistd.h>
-#  include <fcntl.h>
-#  include <sys/socket.h>
-#  include <sys/un.h>
-}
-
-#include "rederrorcodes.h"
-#include "controller.h"
-
-SpiceController::SpiceController():
-    m_client_socket(-1)
-{
-}
-
-SpiceController::SpiceController(const std::string &name):
-    m_client_socket(-1),
-    m_name(name)
-{
-}
-
-SpiceController::~SpiceController()
-{
-    g_debug(G_STRFUNC);
-    Disconnect();
-}
-
-void SpiceController::SetFilename(const std::string &name)
-{
-    m_name = name;
-}
-
-int SpiceController::Connect()
-{
-    // check, if we have a filename for socket to create
-    if (m_name.empty())
-        return -1;
-
-    if (m_client_socket == -1)
-    {
-        if ((m_client_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
-        {
-            g_critical("controller socket: %s", g_strerror(errno));
-            return -1;
-        }
-    }
-
-    struct sockaddr_un remote;
-    remote.sun_family = AF_UNIX;
-    if (m_name.length() + 1 > sizeof(remote.sun_path))
-        return -1;
-    strcpy(remote.sun_path, m_name.c_str());
-
-    int rc = connect(m_client_socket, (struct sockaddr *) &remote, strlen(remote.sun_path) + sizeof(remote.sun_family));
-    if (rc == -1)
-    {
-        g_critical("controller connect: %s", g_strerror(errno));
-    }
-    else
-    {
-        g_debug("controller connected");
-    }
-
-    return rc;
-}
-
-int SpiceController::Connect(const int nRetries)
-{
-    int rc = -1;
-    int sleep_time = 0;
-
-    // try to connect for specified count
-    for (int i = 0; rc != 0 && i < nRetries; ++i)
-    {
-        rc = Connect();
-        sleep(sleep_time);
-        ++sleep_time;
-    }
-
-    return rc;
-}
-
-void SpiceController::Disconnect()
-{
-    // close the socket
-    close(m_client_socket);
-    m_client_socket = -1;
-
-    // delete the temporary file, which is used for the socket
-    unlink(m_name.c_str());
-    m_name.clear();
-}
-
-uint32_t SpiceController::Write(const void *lpBuffer, uint32_t nBytesToWrite)
-{
-    ssize_t len = send(m_client_socket, lpBuffer, nBytesToWrite, 0);
-
-    if (len != (ssize_t)nBytesToWrite)
-    {
-        g_warning("incomplete send, bytes to write = %u, bytes written = %zd: %s",
-                  nBytesToWrite, len, g_strerror(errno));
-    }
-
-    return len;
-}
-
-int SpiceController::TranslateRC(int nRC)
-{
-    switch (nRC)
-    {
-    case SPICEC_ERROR_CODE_SUCCESS:
-        return 0;
-
-    case SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED:
-        return RDP_ERROR_CODE_HOST_NOT_FOUND;
-
-    case SPICEC_ERROR_CODE_CONNECT_FAILED:
-        return RDP_ERROR_CODE_WINSOCK_CONNECT_FAILED;
-
-    case SPICEC_ERROR_CODE_ERROR:
-    case SPICEC_ERROR_CODE_SOCKET_FAILED:
-        return RDP_ERROR_CODE_INTERNAL_ERROR;
-
-    case SPICEC_ERROR_CODE_RECV_FAILED:
-        return RDP_ERROR_RECV_WINSOCK_FAILED;
-
-    case SPICEC_ERROR_CODE_SEND_FAILED:
-        return RDP_ERROR_SEND_WINSOCK_FAILED;
-
-    case SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY:
-        return RDP_ERROR_CODE_OUT_OF_MEMORY;
-
-    case SPICEC_ERROR_CODE_AGENT_TIMEOUT:
-        return RDP_ERROR_CODE_TIMEOUT;
-
-    case SPICEC_ERROR_CODE_AGENT_ERROR:
-        return RDP_ERROR_CODE_INTERNAL_ERROR;
-
-    default:
-        return RDP_ERROR_CODE_INTERNAL_ERROR;
-    }
-}
-
commit d8a79053c72136d75b6137f72cd528cd838a5410
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 11:39:03 2013 +0100

    Use gio to write trust store
    
    This has the big advantage of being portable, in addition to removing
    some code. Big disadvantage is that this adds a dependency on gio.

diff --git a/SpiceXPI/src/plugin/plugin.cpp b/SpiceXPI/src/plugin/plugin.cpp
index b2ea8a2..51d1c09 100644
--- a/SpiceXPI/src/plugin/plugin.cpp
+++ b/SpiceXPI/src/plugin/plugin.cpp
@@ -57,6 +57,8 @@
 #include <sstream>
 #include <signal.h>
 #include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
 
 extern "C" {
 #include <pthread.h>
@@ -188,6 +190,10 @@ nsPluginInstance::nsPluginInstance(NPP aInstance):
     // create temporary directory in /tmp
     char tmp_dir[] = "/tmp/spicec-XXXXXX";
     m_tmp_dir = mkdtemp(tmp_dir);
+
+#if !GLIB_CHECK_VERSION(2, 35, 0)
+    g_type_init();
+#endif
 }
 
 nsPluginInstance::~nsPluginInstance()
@@ -647,41 +653,39 @@ bool nsPluginInstance::StartClient()
     g_return_val_if_reached(false);
 }
 
-bool nsPluginInstance::CreateTrustStore(void)
+bool nsPluginInstance::CreateTrustStoreFile(const std::string &trust_store)
 {
-    // create trust store filename
-    FILE *fp;
-    int fd = -1;
-    char trust_store_template[] = "/tmp/truststore.pem-XXXXXX";
-    mode_t prev_umask = umask(0177);
-    fd = mkstemp(trust_store_template);
-    umask(prev_umask);
-    m_trust_store_file = trust_store_template;
+    GFile *tmp_file;
+    GFileIOStream *iostream;
+    GOutputStream *stream;
 
-    if (fd != -1)
-    {
-        fp = fdopen(fd,"w+");
-        if (fp != NULL)
-        {
-            fputs(m_trust_store.c_str(), fp);
-            fflush(fp);
-            fsync(fd);
-            fclose(fp);
-        }
-        else
-        {
-            g_critical("could not open truststore temp file");
-            close(fd);
-            unlink(m_trust_store_file.c_str());
-            m_trust_store_file.clear();
-            return false;
-        }
+    tmp_file = g_file_new_tmp("trustore.pem-XXXXXX", &iostream, NULL);
+    if (tmp_file == NULL) {
+        g_message("Couldn't create truststore");
+        return false;
     }
-    else
-    {
-        g_critical("could not create truststore temp file: %s", g_strerror(errno));
+
+    stream = g_io_stream_get_output_stream(G_IO_STREAM(iostream));
+    if (!g_output_stream_write_all(stream,
+                                   trust_store.c_str(),
+                                   trust_store.length(),
+                                   NULL, NULL, NULL)) {
+        g_critical("Couldn't write truststore");
         return false;
     }
+    m_trust_store_file = g_file_get_path(tmp_file);
+    g_object_unref(tmp_file);
+    g_object_unref(iostream);
+
+    return true;
+}
+
+bool nsPluginInstance::RemoveTrustStoreFile()
+{
+    if (g_unlink(m_trust_store_file.c_str()) != 0)
+        return false;;
+
+    m_trust_store_file.clear();
 
     return true;
 }
@@ -712,7 +716,7 @@ void nsPluginInstance::Connect()
         return;
     }
 
-    if (!this->CreateTrustStore()) {
+    if (!this->CreateTrustStoreFile(m_trust_store)) {
         g_critical("failed to create trust store");
         return;
     }
@@ -844,8 +848,7 @@ void *nsPluginInstance::ControllerWaitHelper(void *opaque)
         fake_this->m_external_controller.Disconnect();
     }
     
-    unlink(fake_this->m_trust_store_file.c_str());
-    fake_this->m_trust_store_file.clear();
+    fake_this->RemoveTrustStoreFile();
     fake_this->m_pid_controller = -1;
     return NULL;
 }
diff --git a/SpiceXPI/src/plugin/plugin.h b/SpiceXPI/src/plugin/plugin.h
index 9c56f73..ea50ca5 100644
--- a/SpiceXPI/src/plugin/plugin.h
+++ b/SpiceXPI/src/plugin/plugin.h
@@ -187,6 +187,8 @@ private:
 private:
     bool StartClient();
     bool CreateTrustStore();
+    bool CreateTrustStoreFile(const std::string &trust_store);
+    bool RemoveTrustStoreFile();
 
     pid_t m_pid_controller;
     int32_t m_connected_status;
diff --git a/configure.ac b/configure.ac
index 0ca271c..48d3a6b 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)
+PKG_CHECK_MODULES(GLIB, glib-2.0 gio-2.0)
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
commit 1899f83e20fdc447dc29564a5a3b3930bfcaa41c
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Fri Feb 8 11:09:54 2013 +0100

    Split nsPluginInstance::connect a bit
    
    This makes it more readable, and moves OS-specific code to separate
    functions.

diff --git a/SpiceXPI/src/plugin/plugin.cpp b/SpiceXPI/src/plugin/plugin.cpp
index 98f2d8f..b2ea8a2 100644
--- a/SpiceXPI/src/plugin/plugin.cpp
+++ b/SpiceXPI/src/plugin/plugin.cpp
@@ -579,21 +579,8 @@ void nsPluginInstance::SendStr(uint32_t id, std::string str)
     free(msg);
 }
 
-void nsPluginInstance::Connect()
+bool nsPluginInstance::StartClient()
 {
-    const int port = portToInt(m_port);
-    const int sport = portToInt(m_secure_port);
-    if (port < 0)
-        g_warning("invalid port: '%s'", m_port.c_str());
-    if (sport < 0)
-        g_warning("invalid secure port: '%s'", m_secure_port.c_str());
-    if (port <= 0 && sport <= 0)
-    {
-        m_connected_status = 1;
-        CallOnDisconnected(m_connected_status);
-        return;
-    }
-
     std::string socket_file(m_tmp_dir);
     socket_file += "/spice-xpi";
 
@@ -601,7 +588,7 @@ void nsPluginInstance::Connect()
     int pipe_fds[2] = { -1, -1 };
     if (pipe(pipe_fds) < 0) {
         perror("spice-xpi system error");
-        return;
+        return false;
     }
 
     m_pid_controller = fork();
@@ -654,74 +641,109 @@ void nsPluginInstance::Connect()
 
         m_external_controller.SetFilename(socket_file);
 
-        if (m_external_controller.Connect(10) != 0)
-        {
-            g_critical("could not connect to spice client controller");
-            return;
-        }
+        return true;
+    }
 
-        // create trust store filename
-        FILE *fp;
-        int fd = -1;
-        char trust_store_template[] = "/tmp/truststore.pem-XXXXXX";
-        mode_t prev_umask = umask(0177);
-        fd = mkstemp(trust_store_template);
-        umask(prev_umask);
-        m_trust_store_file = trust_store_template;
+    g_return_val_if_reached(false);
+}
+
+bool nsPluginInstance::CreateTrustStore(void)
+{
+    // create trust store filename
+    FILE *fp;
+    int fd = -1;
+    char trust_store_template[] = "/tmp/truststore.pem-XXXXXX";
+    mode_t prev_umask = umask(0177);
+    fd = mkstemp(trust_store_template);
+    umask(prev_umask);
+    m_trust_store_file = trust_store_template;
 
-        if (fd != -1)
+    if (fd != -1)
+    {
+        fp = fdopen(fd,"w+");
+        if (fp != NULL)
         {
-            fp = fdopen(fd,"w+");
-            if (fp != NULL)
-            {
-                fputs(m_trust_store.c_str(), fp);
-                fflush(fp);
-                fsync(fd);
-                fclose(fp);
-            }
-            else
-            {
-                g_critical("could not open truststore temp file");
-                close(fd);
-                unlink(m_trust_store_file.c_str());
-                m_trust_store_file.clear();
-                return;
-            }
+            fputs(m_trust_store.c_str(), fp);
+            fflush(fp);
+            fsync(fd);
+            fclose(fp);
         }
         else
         {
-            g_critical("could not create truststore temp file: %s", g_strerror(errno));
-            return;
+            g_critical("could not open truststore temp file");
+            close(fd);
+            unlink(m_trust_store_file.c_str());
+            m_trust_store_file.clear();
+            return false;
         }
+    }
+    else
+    {
+        g_critical("could not create truststore temp file: %s", g_strerror(errno));
+        return false;
+    }
+
+    return true;
+}
 
-        SendInit();
-        SendStr(CONTROLLER_HOST, m_host_ip);
-        if (port > 0)
-            SendValue(CONTROLLER_PORT, port);
-        if (sport > 0)
-            SendValue(CONTROLLER_SPORT, sport);
-        SendValue(CONTROLLER_FULL_SCREEN,
-                   (m_fullscreen == true ? CONTROLLER_SET_FULL_SCREEN : 0) |
-                   (m_admin_console == false ? CONTROLLER_AUTO_DISPLAY_RES : 0));
-        SendBool(CONTROLLER_ENABLE_SMARTCARD, m_smartcard);
-        SendStr(CONTROLLER_PASSWORD, m_password);
-        SendStr(CONTROLLER_TLS_CIPHERS, m_cipher_suite);
-        SendStr(CONTROLLER_SET_TITLE, m_title);
-        SendBool(CONTROLLER_SEND_CAD, m_send_ctrlaltdel);
-        SendBool(CONTROLLER_ENABLE_USB_AUTOSHARE, m_usb_auto_share);
-        SendStr(CONTROLLER_USB_FILTER, m_usb_filter);
-        SendStr(CONTROLLER_SECURE_CHANNELS, m_ssl_channels);
-        SendStr(CONTROLLER_CA_FILE, m_trust_store_file);
-        SendStr(CONTROLLER_HOST_SUBJECT, m_host_subject);
-        SendStr(CONTROLLER_HOTKEYS, m_hot_keys);
-        SendValue(CONTROLLER_COLOR_DEPTH, atoi(m_color_depth.c_str()));
-        SendStr(CONTROLLER_DISABLE_EFFECTS, m_disable_effects);
-        SendMsg(CONTROLLER_CONNECT);
-        SendMsg(CONTROLLER_SHOW);
-
-        // set connected status
-        m_connected_status = -1;
+void nsPluginInstance::Connect()
+{
+    const int port = portToInt(m_port);
+    const int sport = portToInt(m_secure_port);
+    if (port < 0)
+        g_warning("invalid port: '%s'", m_port.c_str());
+    if (sport < 0)
+        g_warning("invalid secure port: '%s'", m_secure_port.c_str());
+    if (port <= 0 && sport <= 0)
+    {
+        m_connected_status = 1;
+        CallOnDisconnected(m_connected_status);
+        return;
     }
+
+    if (!this->StartClient()) {
+        g_critical("failed to start SPICE client");
+        return;
+    }
+
+    if (m_external_controller.Connect(10) != 0)
+    {
+        g_critical("could not connect to spice client controller");
+        return;
+    }
+
+    if (!this->CreateTrustStore()) {
+        g_critical("failed to create trust store");
+        return;
+    }
+
+    SendInit();
+    SendStr(CONTROLLER_HOST, m_host_ip);
+    if (port > 0)
+        SendValue(CONTROLLER_PORT, port);
+    if (sport > 0)
+        SendValue(CONTROLLER_SPORT, sport);
+    SendValue(CONTROLLER_FULL_SCREEN,
+            (m_fullscreen == true ? CONTROLLER_SET_FULL_SCREEN : 0) |
+            (m_admin_console == false ? CONTROLLER_AUTO_DISPLAY_RES : 0));
+    SendBool(CONTROLLER_ENABLE_SMARTCARD, m_smartcard);
+    SendStr(CONTROLLER_PASSWORD, m_password);
+    SendStr(CONTROLLER_TLS_CIPHERS, m_cipher_suite);
+    SendStr(CONTROLLER_SET_TITLE, m_title);
+    SendBool(CONTROLLER_SEND_CAD, m_send_ctrlaltdel);
+    SendBool(CONTROLLER_ENABLE_USB_AUTOSHARE, m_usb_auto_share);
+    SendStr(CONTROLLER_USB_FILTER, m_usb_filter);
+    SendStr(CONTROLLER_SECURE_CHANNELS, m_ssl_channels);
+    SendStr(CONTROLLER_CA_FILE, m_trust_store_file);
+    SendStr(CONTROLLER_HOST_SUBJECT, m_host_subject);
+    SendStr(CONTROLLER_HOTKEYS, m_hot_keys);
+    SendValue(CONTROLLER_COLOR_DEPTH, atoi(m_color_depth.c_str()));
+    SendStr(CONTROLLER_DISABLE_EFFECTS, m_disable_effects);
+    SendMsg(CONTROLLER_CONNECT);
+    SendMsg(CONTROLLER_SHOW);
+
+    // set connected status
+    m_connected_status = -1;
 }
 
 void nsPluginInstance::Show()
diff --git a/SpiceXPI/src/plugin/plugin.h b/SpiceXPI/src/plugin/plugin.h
index 35734a8..9c56f73 100644
--- a/SpiceXPI/src/plugin/plugin.h
+++ b/SpiceXPI/src/plugin/plugin.h
@@ -185,6 +185,9 @@ private:
     void CallOnDisconnected(int code);
   
 private:
+    bool StartClient();
+    bool CreateTrustStore();
+
     pid_t m_pid_controller;
     int32_t m_connected_status;
     SpiceController m_external_controller;


More information about the Spice-commits mailing list