summaryrefslogtreecommitdiff
path: root/Source/cmServerConnection.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmServerConnection.cxx')
-rw-r--r--Source/cmServerConnection.cxx377
1 files changed, 377 insertions, 0 deletions
diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx
new file mode 100644
index 000000000..36312edf4
--- /dev/null
+++ b/Source/cmServerConnection.cxx
@@ -0,0 +1,377 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmServerConnection.h"
+
+#include "cmFileMonitor.h"
+#include "cmServer.h"
+#include "cmServerDictionary.h"
+
+#include <assert.h>
+#include <string.h>
+
+namespace {
+
+struct write_req_t
+{
+ uv_write_t req;
+ uv_buf_t buf;
+};
+
+void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
+{
+ (void)(handle);
+ char* rawBuffer = new char[suggested_size];
+ *buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size));
+}
+
+void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
+{
+ auto conn = reinterpret_cast<cmServerConnection*>(stream->data);
+ if (nread >= 0) {
+ conn->ReadData(std::string(buf->base, buf->base + nread));
+ } else {
+ conn->TriggerShutdown();
+ }
+
+ delete[](buf->base);
+}
+
+void on_write(uv_write_t* req, int status)
+{
+ (void)(status);
+ auto conn = reinterpret_cast<cmServerConnection*>(req->data);
+
+ // Free req and buffer
+ write_req_t* wr = reinterpret_cast<write_req_t*>(req);
+ delete[](wr->buf.base);
+ delete wr;
+
+ conn->ProcessNextRequest();
+}
+
+void on_new_connection(uv_stream_t* stream, int status)
+{
+ (void)(status);
+ auto conn = reinterpret_cast<cmServerConnection*>(stream->data);
+ conn->Connect(stream);
+}
+
+void on_signal(uv_signal_t* signal, int signum)
+{
+ auto conn = reinterpret_cast<cmServerConnection*>(signal->data);
+ (void)(signum);
+ conn->TriggerShutdown();
+}
+
+void on_signal_close(uv_handle_t* handle)
+{
+ delete reinterpret_cast<uv_signal_t*>(handle);
+}
+
+void on_pipe_close(uv_handle_t* handle)
+{
+ delete reinterpret_cast<uv_pipe_t*>(handle);
+}
+
+void on_tty_close(uv_handle_t* handle)
+{
+ delete reinterpret_cast<uv_tty_t*>(handle);
+}
+
+} // namespace
+
+class LoopGuard
+{
+public:
+ LoopGuard(cmServerConnection* connection)
+ : Connection(connection)
+ {
+ this->Connection->mLoop = uv_default_loop();
+ if (!this->Connection->mLoop) {
+ return;
+ }
+ this->Connection->mFileMonitor =
+ new cmFileMonitor(this->Connection->mLoop);
+ }
+
+ ~LoopGuard()
+ {
+ if (!this->Connection->mLoop) {
+ return;
+ }
+
+ if (this->Connection->mFileMonitor) {
+ delete this->Connection->mFileMonitor;
+ }
+ uv_loop_close(this->Connection->mLoop);
+ this->Connection->mLoop = nullptr;
+ }
+
+private:
+ cmServerConnection* Connection;
+};
+
+cmServerConnection::cmServerConnection()
+{
+}
+
+cmServerConnection::~cmServerConnection()
+{
+}
+
+void cmServerConnection::SetServer(cmServer* s)
+{
+ this->Server = s;
+}
+
+bool cmServerConnection::ProcessEvents(std::string* errorMessage)
+{
+ assert(this->Server);
+ errorMessage->clear();
+
+ this->RawReadBuffer.clear();
+ this->RequestBuffer.clear();
+
+ LoopGuard guard(this);
+ (void)(guard);
+ if (!this->mLoop) {
+ *errorMessage = "Internal Error: Failed to create event loop.";
+ return false;
+ }
+
+ this->SIGINTHandler = new uv_signal_t;
+ uv_signal_init(this->mLoop, this->SIGINTHandler);
+ this->SIGINTHandler->data = static_cast<void*>(this);
+ uv_signal_start(this->SIGINTHandler, &on_signal, SIGINT);
+
+ this->SIGHUPHandler = new uv_signal_t;
+ uv_signal_init(this->mLoop, this->SIGHUPHandler);
+ this->SIGHUPHandler->data = static_cast<void*>(this);
+ uv_signal_start(this->SIGHUPHandler, &on_signal, SIGHUP);
+
+ if (!DoSetup(errorMessage)) {
+ return false;
+ }
+
+ if (uv_run(this->mLoop, UV_RUN_DEFAULT) != 0) {
+ *errorMessage = "Internal Error: Event loop stopped in unclean state.";
+ return false;
+ }
+
+ // These need to be cleaned up by now:
+ assert(!this->ReadStream);
+ assert(!this->WriteStream);
+
+ this->RawReadBuffer.clear();
+ this->RequestBuffer.clear();
+
+ return true;
+}
+
+void cmServerConnection::ReadData(const std::string& data)
+{
+ this->RawReadBuffer += data;
+
+ for (;;) {
+ auto needle = this->RawReadBuffer.find('\n');
+
+ if (needle == std::string::npos) {
+ return;
+ }
+ std::string line = this->RawReadBuffer.substr(0, needle);
+ const auto ls = line.size();
+ if (ls > 1 && line.at(ls - 1) == '\r') {
+ line.erase(ls - 1, 1);
+ }
+ this->RawReadBuffer.erase(this->RawReadBuffer.begin(),
+ this->RawReadBuffer.begin() +
+ static_cast<long>(needle) + 1);
+ if (line == kSTART_MAGIC) {
+ this->RequestBuffer.clear();
+ continue;
+ }
+ if (line == kEND_MAGIC) {
+ this->Server->QueueRequest(this->RequestBuffer);
+ this->RequestBuffer.clear();
+ } else {
+ this->RequestBuffer += line;
+ this->RequestBuffer += "\n";
+ }
+ }
+}
+
+void cmServerConnection::TriggerShutdown()
+{
+ this->FileMonitor()->StopMonitoring();
+
+ uv_signal_stop(this->SIGINTHandler);
+ uv_signal_stop(this->SIGHUPHandler);
+
+ uv_close(reinterpret_cast<uv_handle_t*>(this->SIGINTHandler),
+ &on_signal_close); // delete handle
+ uv_close(reinterpret_cast<uv_handle_t*>(this->SIGHUPHandler),
+ &on_signal_close); // delete handle
+
+ this->SIGINTHandler = nullptr;
+ this->SIGHUPHandler = nullptr;
+
+ this->TearDown();
+}
+
+void cmServerConnection::WriteData(const std::string& data)
+{
+ assert(this->WriteStream);
+
+ auto ds = data.size();
+
+ write_req_t* req = new write_req_t;
+ req->req.data = this;
+ req->buf = uv_buf_init(new char[ds], static_cast<unsigned int>(ds));
+ memcpy(req->buf.base, data.c_str(), ds);
+
+ uv_write(reinterpret_cast<uv_write_t*>(req),
+ static_cast<uv_stream_t*>(this->WriteStream), &req->buf, 1,
+ on_write);
+}
+
+void cmServerConnection::ProcessNextRequest()
+{
+ Server->PopOne();
+}
+
+void cmServerConnection::SendGreetings()
+{
+ Server->PrintHello();
+}
+
+cmServerStdIoConnection::cmServerStdIoConnection()
+{
+ this->Input.tty = nullptr;
+ this->Output.tty = nullptr;
+}
+
+bool cmServerStdIoConnection::DoSetup(std::string* errorMessage)
+{
+ (void)(errorMessage);
+
+ if (uv_guess_handle(1) == UV_TTY) {
+ usesTty = true;
+ this->Input.tty = new uv_tty_t;
+ uv_tty_init(this->Loop(), this->Input.tty, 0, 1);
+ uv_tty_set_mode(this->Input.tty, UV_TTY_MODE_NORMAL);
+ Input.tty->data = this;
+ this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.tty);
+
+ this->Output.tty = new uv_tty_t;
+ uv_tty_init(this->Loop(), this->Output.tty, 1, 0);
+ uv_tty_set_mode(this->Output.tty, UV_TTY_MODE_NORMAL);
+ Output.tty->data = this;
+ this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.tty);
+ } else {
+ usesTty = false;
+ this->Input.pipe = new uv_pipe_t;
+ uv_pipe_init(this->Loop(), this->Input.pipe, 0);
+ uv_pipe_open(this->Input.pipe, 0);
+ Input.pipe->data = this;
+ this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.pipe);
+
+ this->Output.pipe = new uv_pipe_t;
+ uv_pipe_init(this->Loop(), this->Output.pipe, 0);
+ uv_pipe_open(this->Output.pipe, 1);
+ Output.pipe->data = this;
+ this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.pipe);
+ }
+
+ SendGreetings();
+ uv_read_start(this->ReadStream, on_alloc_buffer, on_read);
+
+ return true;
+}
+
+void cmServerStdIoConnection::TearDown()
+{
+ if (usesTty) {
+ uv_close(reinterpret_cast<uv_handle_t*>(this->Input.tty), &on_tty_close);
+ uv_close(reinterpret_cast<uv_handle_t*>(this->Output.tty), &on_tty_close);
+ this->Input.tty = nullptr;
+ this->Output.tty = nullptr;
+ } else {
+ uv_close(reinterpret_cast<uv_handle_t*>(this->Input.pipe), &on_pipe_close);
+ uv_close(reinterpret_cast<uv_handle_t*>(this->Output.pipe),
+ &on_pipe_close);
+ this->Input.pipe = nullptr;
+ this->Input.pipe = nullptr;
+ }
+ this->ReadStream = nullptr;
+ this->WriteStream = nullptr;
+}
+
+cmServerPipeConnection::cmServerPipeConnection(const std::string& name)
+ : PipeName(name)
+{
+}
+
+bool cmServerPipeConnection::DoSetup(std::string* errorMessage)
+{
+ this->ServerPipe = new uv_pipe_t;
+ uv_pipe_init(this->Loop(), this->ServerPipe, 0);
+ this->ServerPipe->data = this;
+
+ int r;
+ if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) {
+ *errorMessage = std::string("Internal Error with ") + this->PipeName +
+ ": " + uv_err_name(r);
+ return false;
+ }
+ auto serverStream = reinterpret_cast<uv_stream_t*>(this->ServerPipe);
+ if ((r = uv_listen(serverStream, 1, on_new_connection)) != 0) {
+ *errorMessage = std::string("Internal Error listening on ") +
+ this->PipeName + ": " + uv_err_name(r);
+ return false;
+ }
+
+ return true;
+}
+
+void cmServerPipeConnection::TearDown()
+{
+ if (this->ClientPipe) {
+ uv_close(reinterpret_cast<uv_handle_t*>(this->ClientPipe), &on_pipe_close);
+ this->WriteStream->data = nullptr;
+ }
+ uv_close(reinterpret_cast<uv_handle_t*>(this->ServerPipe), &on_pipe_close);
+
+ this->ClientPipe = nullptr;
+ this->ServerPipe = nullptr;
+ this->WriteStream = nullptr;
+ this->ReadStream = nullptr;
+}
+
+void cmServerPipeConnection::Connect(uv_stream_t* server)
+{
+ if (this->ClientPipe) {
+ // Accept and close all pipes but the first:
+ uv_pipe_t* rejectPipe = new uv_pipe_t;
+
+ uv_pipe_init(this->Loop(), rejectPipe, 0);
+ auto rejecter = reinterpret_cast<uv_stream_t*>(rejectPipe);
+ uv_accept(server, rejecter);
+ uv_close(reinterpret_cast<uv_handle_t*>(rejecter), &on_pipe_close);
+ return;
+ }
+
+ this->ClientPipe = new uv_pipe_t;
+ uv_pipe_init(this->Loop(), this->ClientPipe, 0);
+ this->ClientPipe->data = this;
+ auto client = reinterpret_cast<uv_stream_t*>(this->ClientPipe);
+ if (uv_accept(server, client) != 0) {
+ uv_close(reinterpret_cast<uv_handle_t*>(client), nullptr);
+ return;
+ }
+ this->ReadStream = client;
+ this->WriteStream = client;
+
+ uv_read_start(this->ReadStream, on_alloc_buffer, on_read);
+
+ this->SendGreetings();
+}