diff options
author | Christophe Fergeau <cfergeau@redhat.com> | 2012-09-29 21:02:00 +0200 |
---|---|---|
committer | Christophe Fergeau <cfergeau@redhat.com> | 2012-10-02 23:10:05 +0200 |
commit | ab8306e8cad0fa62009248b80990a1d1fabb7f33 (patch) | |
tree | 5a582374d1ceddee53b38fe539ab70523be46bb4 | |
parent | 688996a4c12395739e41745e345096fca50bd0ed (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.cpp | 182 | ||||
-rw-r--r-- | SPICEConsoleAPI.h | 21 |
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 §ion, 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 |