summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Fergeau <cfergeau@redhat.com>2012-09-29 21:02:00 +0200
committerChristophe Fergeau <cfergeau@redhat.com>2012-10-02 23:10:05 +0200
commitab8306e8cad0fa62009248b80990a1d1fabb7f33 (patch)
tree5a582374d1ceddee53b38fe539ab70523be46bb4
parent688996a4c12395739e41745e345096fca50bd0ed (diff)
Implement ::connect() and ::disconnect()
The implementation is directly imported from spice-xpi. The value of the various attributes is not yet sent to the external controller.
-rw-r--r--SPICEConsoleAPI.cpp182
-rw-r--r--SPICEConsoleAPI.h21
2 files changed, 199 insertions, 4 deletions
diff --git a/SPICEConsoleAPI.cpp b/SPICEConsoleAPI.cpp
index 091e47c..5c3be8a 100644
--- a/SPICEConsoleAPI.cpp
+++ b/SPICEConsoleAPI.cpp
@@ -33,6 +33,10 @@
*
* ***** END LICENSE BLOCK ***** */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
#include <glib.h>
#include "JSObject.h"
@@ -42,6 +46,18 @@
#include "SPICEConsoleAPI.h"
+// helper function for tcp/udp range conversion and validation
+static int portToInt(const std::string &port)
+{
+ errno = 0;
+ char *end;
+ const long int min = 0;
+ const long int max = 65535;
+ long int conv = strtol(port.c_str(), &end, 10);
+ return (errno || *end != '\0' || end == port.c_str() || conv < min || conv > max)
+ ? -1 : static_cast<int>(conv);
+}
+
///////////////////////////////////////////////////////////////////////////////
/// @fn SPICEConsolePtr SPICEConsoleAPI::getPlugin()
///
@@ -63,7 +79,135 @@ SPICEConsolePtr SPICEConsoleAPI::getPlugin()
// Methods
void SPICEConsoleAPI::connect()
-{}
+{
+ int port = 0;
+ int sport = 0;
+ try {
+ port = getAttribute("port").cast<int>();
+ sport = getAttribute("SecurePort").cast<int>();
+ } catch (FB::bad_variant_cast &e) {
+ g_warning("invalid port/SecurePort value");
+ }
+
+ if (port < 0)
+ g_warning("invalid port: '%d'", port);
+ if (sport < 0)
+ g_warning("invalid secure port: '%d'", sport);
+
+ if (port <= 0 && sport <= 0)
+ {
+ m_connected_status = 1;
+ fire_disconnected(m_connected_status);
+ return;
+ }
+
+ std::string socket_file(m_tmp_dir);
+ socket_file += "/spice-xpi";
+ if (setenv("SPICE_XPI_SOCKET", socket_file.c_str(), 1))
+ {
+ g_critical("could not set SPICE_XPI_SOCKET env variable");
+ return;
+ }
+
+ /* 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;
+ }
+
+ 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;
+
+ execl("/usr/libexec/spice-xpi-client", "/usr/libexec/spice-xpi-client", NULL);
+ g_message("failed to run spice-xpi-client, running spicec instead");
+
+ // TODO: temporary fallback for backward compatibility
+ execl("/usr/bin/spicec", "/usr/bin/spicec", "--controller", NULL);
+ g_critical("ERROR failed to run spicec fallback");
+
+ 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);
+
+ if (m_external_controller.Connect(10) != 0)
+ {
+ g_critical("could not connect to spice client controller");
+ return;
+ }
+
+ // 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)
+ {
+ fp = fdopen(fd,"w+");
+ if (fp != NULL)
+ {
+ std::string trust_store;
+ try {
+ trust_store = getAttribute("TrustStore").cast<std::string>();
+ } catch (FB::bad_variant_cast &e) {
+ g_warning("invalid trust store value");
+ }
+ fputs(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;
+ }
+ }
+ else
+ {
+ g_critical("could not create truststore temp file: %s", g_strerror(errno));
+ return;
+ }
+
+ SendInit();
+
+ // set connected status
+ m_connected_status = -1;
+ }
+}
void SPICEConsoleAPI::show()
{
@@ -72,7 +216,10 @@ void SPICEConsoleAPI::show()
}
void SPICEConsoleAPI::disconnect()
-{}
+{
+ if (m_pid_controller > 0)
+ kill(-m_pid_controller, SIGTERM);
+}
void SPICEConsoleAPI::SetLanguageStrings(const std::string &section,
const std::string &lang)
@@ -131,8 +278,39 @@ void SPICEConsoleAPI::WriteToPipe(const void *data, uint32_t size)
m_external_controller.Write(data, size);
}
+void SPICEConsoleAPI::SendInit()
+{
+ ControllerInit msg = { {CONTROLLER_MAGIC, CONTROLLER_VERSION, sizeof(msg)},
+ 0, CONTROLLER_FLAG_EXCLUSIVE };
+ WriteToPipe(&msg, sizeof(msg));
+}
+
void SPICEConsoleAPI::SendMsg(uint32_t id)
{
ControllerMsg msg = {id, sizeof(msg)};
WriteToPipe(&msg, sizeof(msg));
}
+
+void *SPICEConsoleAPI::ControllerWaitHelper(void *opaque)
+{
+ SPICEConsoleAPI *fake_this = reinterpret_cast<SPICEConsoleAPI *>(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);
+ if (!getenv("SPICE_XPI_DEBUG"))
+ {
+ fake_this->fire_disconnected(exit_code);
+ fake_this->m_external_controller.Disconnect();
+ }
+
+ unlink(fake_this->m_trust_store_file.c_str());
+ fake_this->m_trust_store_file.clear();
+ fake_this->m_pid_controller = -1;
+ return NULL;
+}
+
diff --git a/SPICEConsoleAPI.h b/SPICEConsoleAPI.h
index 0e627e8..33358e5 100644
--- a/SPICEConsoleAPI.h
+++ b/SPICEConsoleAPI.h
@@ -59,7 +59,10 @@ public:
/// @see FB::JSAPIAuto::registerEvent
////////////////////////////////////////////////////////////////////////////
SPICEConsoleAPI(const SPICEConsolePtr& plugin, const FB::BrowserHostPtr& host) :
- m_plugin(plugin), m_host(host), m_connected_status(-2)
+ m_plugin(plugin),
+ m_host(host),
+ m_connected_status(-2),
+ m_pid_controller(-1)
{
registerMethod("connect", make_method(this, &SPICEConsoleAPI::connect));
registerMethod("show", make_method(this, &SPICEConsoleAPI::show));
@@ -93,6 +96,10 @@ public:
registerAttribute("UsbAutoShare", true);
registerAttribute("ColorDepth", "");
registerAttribute("DisableEffects", "");
+
+ // create temporary directory in /tmp
+ char tmp_dir[] = "/tmp/spicec-XXXXXX";
+ m_tmp_dir = mkdtemp(tmp_dir);
}
///////////////////////////////////////////////////////////////////////////////
@@ -102,7 +109,11 @@ public:
/// the browser is done with it; this will almost definitely be after
/// the plugin is released.
///////////////////////////////////////////////////////////////////////////////
- virtual ~SPICEConsoleAPI() {};
+ virtual ~SPICEConsoleAPI()
+ {
+ // delete the temporary directory used for a client socket
+ rmdir(m_tmp_dir.c_str());
+ };
SPICEConsolePtr getPlugin();
@@ -128,14 +139,20 @@ private:
std::map<std::string, std::string> m_languages;
std::string m_usb_filter;
int m_connected_status;
+ pid_t m_pid_controller;
SpiceController m_external_controller;
+ std::string m_tmp_dir;
+ std::string m_trust_store_file;
/* Properties */
std::string m_SSLChannels;
// Controller communication
void WriteToPipe(const void *data, uint32_t size);
+ void SendInit();
void SendMsg(uint32_t id);
+
+ static void *ControllerWaitHelper(void *opaque);
};
#endif // H_SPICEConsoleAPI