diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index dfdb9274..3cb2410b 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -22,6 +22,7 @@ set(inc ../../lib/base ../../lib/common ../../lib/io + ../../lib/ipc ../../lib/mt ../../lib/net ../../lib/platform @@ -47,7 +48,7 @@ if (VNC_SUPPORT) endif() target_link_libraries(synergyd - arch base common io mt net platform synergy ${libs}) + arch base common io ipc mt net platform synergy ${libs}) if (CONF_CPACK) install(TARGETS diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 13464d4e..e86d1c08 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -36,7 +36,8 @@ SOURCES += src/main.cpp \ src/QSynergyApplication.cpp \ src/VersionChecker.cpp \ src/SetupWizard.cpp \ - src/IpcLogReader.cpp + src/IpcLogReader.cpp \ + src/IpcClient.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -59,7 +60,8 @@ HEADERS += src/MainWindow.h \ src/QSynergyApplication.h \ src/VersionChecker.h \ src/SetupWizard.h \ - src/IpcLogReader.h + src/IpcLogReader.h \ + src/IpcClient.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc TRANSLATIONS = res/lang/nl_NL.ts diff --git a/src/gui/src/IpcClient.cpp b/src/gui/src/IpcClient.cpp new file mode 100644 index 00000000..ee0f9012 --- /dev/null +++ b/src/gui/src/IpcClient.cpp @@ -0,0 +1,83 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Nick Bolton + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "IpcClient.h" +#include +#include + +IpcClient::IpcClient() +{ + m_Socket = new QTcpSocket(this); + connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); + connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError))); +} + +IpcClient::~IpcClient() +{ +} + +void IpcClient::connectToHost() +{ + m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), IPC_PORT); +} + +void IpcClient::read() +{ + QDataStream stream(m_Socket); + + char codeBuf[1]; + stream.readRawData(codeBuf, 1); + + switch (codeBuf[0]) { + case kIpcLogLine: { + char lenBuf[1]; + stream.readRawData(lenBuf, 1); + + char* data = new char[lenBuf[0] + 1]; + stream.readRawData(data, lenBuf[0]); + data[(int)lenBuf[0]] = 0; + + QString s(data); + readLogLine(s); + } + break; + } +} + +void IpcClient::error(QAbstractSocket::SocketError error) +{ + errorMessage("ERROR: Could not connect to background service."); +} + +void IpcClient::write(unsigned char code, unsigned char length, const char* data) +{ + QDataStream stream(m_Socket); + + char codeBuf[1]; + codeBuf[0] = code; + stream.writeRawData(codeBuf, 1); + + switch (code) { + case kIpcCommand: { + char lenBuf[1]; + lenBuf[0] = length; + stream.writeRawData(lenBuf, 1); + stream.writeRawData(data, length); + } + break; + } +} diff --git a/src/lib/arch/CArchIpcLogWindows.h b/src/gui/src/IpcClient.h similarity index 55% rename from src/lib/arch/CArchIpcLogWindows.h rename to src/gui/src/IpcClient.h index 85158a0e..5786f070 100644 --- a/src/lib/arch/CArchIpcLogWindows.h +++ b/src/gui/src/IpcClient.h @@ -1,48 +1,53 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Nick Bolton - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#define WIN32_LEAN_AND_MEAN - -#include "IArchLog.h" -#include - -#define ARCH_IPC_LOG CArchIpcLogWindows - -class CThread; - -//! Win32 implementation of IArchLog (IPC version) -class CArchIpcLogWindows : public IArchLog { -public: - CArchIpcLogWindows(); - virtual ~CArchIpcLogWindows(); - - // IArchLog overrides - virtual void openLog(const char* name); - virtual void closeLog(); - virtual void showLog(bool showIfEmpty); - virtual void writeLog(ELevel, const char*); - -private: - void connectThread(void*); - -private: - HANDLE m_pipe; - CThread* m_listenThread; - bool m_connected; -}; +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Nick Bolton + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#define IPC_PORT 24801 + +class QTcpSocket; + +class IpcClient : public QObject +{ + Q_OBJECT + +public: + IpcClient(); + virtual ~IpcClient(); + + void connectToHost(); + void write(unsigned char code, unsigned char length, const char* data); + +private slots: + void read(); + void error(QAbstractSocket::SocketError error); + +signals: + void readLogLine(const QString& text); + void errorMessage(const QString& text); + +private: + QTcpSocket* m_Socket; +}; + +enum EIpcMessage { + kIpcLogLine, + kIpcCommand +}; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 3c12a142..8574490d 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -17,6 +17,8 @@ #define WEBSITE_ADDRESS "synergy-foss.org" +#include + #include "MainWindow.h" #include "AboutDialog.h" #include "ServerConfigDialog.h" @@ -88,8 +90,10 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : if (appConfig.processMode() == Service) { - connect(&m_IpcLogReader, SIGNAL(receivedLine(const QString&)), this, SLOT(appendLog(const QString&))); - m_IpcLogReader.start(); + connect(&m_IpcClient, SIGNAL(readLogLine(const QString&)), this, SLOT(appendLog(const QString&))); + connect(&m_IpcClient, SIGNAL(errorMessage(const QString&)), this, SLOT(appendLog(const QString&))); + m_IpcClient.connectToHost(); + appendLog("INFO: Connecting to background service..."); } } @@ -665,55 +669,9 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() void MainWindow::sendDaemonCommand(const QString& command, bool showErrors) { - sendIpcMessage(Command, command.toStdString().c_str(), showErrors); -} - -// TODO: put this in an IPC client class. -void MainWindow::sendIpcMessage(qIpcMessage type, const char* data, bool showErrors) -{ -#if defined(Q_OS_WIN) - - const WCHAR* name = L"\\\\.\\pipe\\Synergy"; - char message[1024]; - message[0] = type; - char* messagePtr = message; - messagePtr++; - strcpy(messagePtr, data); - - HANDLE pipe = CreateFile( - name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - - if (showErrors && pipe == INVALID_HANDLE_VALUE) - { - appendLog(QString("ERROR: could not connect to service, error: ") + - QString::number(GetLastError())); - return; - } - - DWORD dwMode = PIPE_READMODE_MESSAGE; - BOOL stateSuccess = SetNamedPipeHandleState(pipe, &dwMode, NULL, NULL); - - if (showErrors && !stateSuccess) - { - appendLog(QString("ERROR: could not set service pipe state, error: ") + - QString::number(GetLastError())); - return; - } - - DWORD written; - BOOL writeSuccess = WriteFile( - pipe, message, strlen(message), &written, NULL); - - if (showErrors && !writeSuccess) - { - appendLog(QString("ERROR: could not write to service pipe, error: ") + - QString::number(GetLastError())); - return; - } - - CloseHandle(pipe); - -#endif + std::string s = command.toStdString(); + const char* data = s.c_str(); + m_IpcClient.write(Command, strlen(data), data); } void MainWindow::on_m_pActionWizard_triggered() diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index d0dfb9b7..6c2bf76a 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -30,7 +30,7 @@ #include "ServerConfig.h" #include "AppConfig.h" #include "VersionChecker.h" -#include "IpcLogReader.h" +#include "IpcClient.h" class QAction; class QMenu; @@ -142,7 +142,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool m_alreadyHidden; VersionChecker m_versionChecker; SetupWizard* m_SetupWizard; - IpcLogReader m_IpcLogReader; + IpcClient m_IpcClient; }; #endif diff --git a/src/lib/arch/CArch.h b/src/lib/arch/CArch.h index a6efe54c..c011c95d 100644 --- a/src/lib/arch/CArch.h +++ b/src/lib/arch/CArch.h @@ -42,7 +42,6 @@ # include "CArchDaemonWindows.h" # include "CArchFileWindows.h" # include "CArchLogWindows.h" -# include "CArchIpcLogWindows.h" # include "CArchMiscWindows.h" # include "CArchMultithreadWindows.h" # include "CArchNetworkWinsock.h" @@ -57,7 +56,6 @@ # include "CArchDaemonUnix.h" # include "CArchFileUnix.h" # include "CArchLogUnix.h" -# include "CArchIpcLogUnix.h" # if HAVE_PTHREAD # include "CArchMultithreadPosix.h" # endif @@ -111,7 +109,6 @@ public: */ static CArch* getInstance(); - ARCH_IPC_LOG& ipcLog() const { return (ARCH_IPC_LOG&)m_ipcLog; } ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; } private: @@ -120,7 +117,6 @@ private: private: static CArch* s_instance; - ARCH_IPC_LOG m_ipcLog; ARCH_PLUGIN m_plugin; }; diff --git a/src/lib/arch/CArchIpcLogWindows.cpp b/src/lib/arch/CArchIpcLogWindows.cpp deleted file mode 100644 index 8cf02104..00000000 --- a/src/lib/arch/CArchIpcLogWindows.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "CArchIpcLogWindows.h" -#include "CArchMiscWindows.h" -#include "XArch.h" -#include "CThread.h" -#include "TMethodJob.h" -#include "CArch.h" - -#define WIN32_LEAN_AND_MEAN -#include - -// -// CArchIpcLogWindows -// - -CArchIpcLogWindows::CArchIpcLogWindows() : - m_pipe(NULL), - m_listenThread(NULL), - m_connected(false) -{ -} - -CArchIpcLogWindows::~CArchIpcLogWindows() -{ - if (m_listenThread != NULL) - delete m_listenThread; -} - -void -CArchIpcLogWindows::openLog(const char* name) -{ - // grant access to everyone. - SECURITY_DESCRIPTOR sd; - InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, TRUE, static_cast(0), FALSE); - - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = &sd; - - HANDLE pipe = CreateNamedPipe( - TEXT("\\\\.\\pipe\\SynergyLog"), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - 1024, 1024, 0, &sa); - - if (pipe == INVALID_HANDLE_VALUE) - XArch("could not create named pipe."); - - m_pipe = pipe; - - m_listenThread = new CThread(new TMethodJob( - this, &CArchIpcLogWindows::connectThread, nullptr)); -} - -void -CArchIpcLogWindows::connectThread(void*) -{ - // HACK: this seems like a hacky pile of bollocks. it will continuously call - // ConnectNamedPipe every second. if there is no client, it will block, - // but if there is a client it will return FALSE and GetLastError() will - // be ERROR_PIPE_CONNECTED. in any other case, the client has gone away - // and ConnectNamedPipe will go back to blocking (waiting for the client - // to reconnect). - while (true) { - BOOL result = ConnectNamedPipe(m_pipe, NULL); - if ((result == TRUE) || (GetLastError() == ERROR_PIPE_CONNECTED)) { - m_connected = true; - ARCH->sleep(1); - } else { - DisconnectNamedPipe(m_pipe); - } - } -} - -void -CArchIpcLogWindows::closeLog() -{ -} - -void -CArchIpcLogWindows::showLog(bool) -{ -} - -void -CArchIpcLogWindows::writeLog(ELevel level, const char* data) -{ - if (!m_connected) - return; - - DWORD bytesWritten; - WriteFile(m_pipe, data, (DWORD)strlen(data), &bytesWritten, NULL); -} diff --git a/src/lib/arch/CMakeLists.txt b/src/lib/arch/CMakeLists.txt index 34337ae4..adce4c37 100644 --- a/src/lib/arch/CMakeLists.txt +++ b/src/lib/arch/CMakeLists.txt @@ -27,7 +27,6 @@ if (WIN32) CArchDaemonWindows.h CArchFileWindows.h CArchLogWindows.h - CArchIpcLogWindows.h CArchMiscWindows.h CArchMultithreadWindows.h CArchNetworkWinsock.h @@ -51,7 +50,6 @@ if (WIN32) CArchDaemonWindows.cpp CArchFileWindows.cpp CArchLogWindows.cpp - CArchIpcLogWindows.cpp CArchMiscWindows.cpp CArchMultithreadWindows.cpp CArchNetworkWinsock.cpp @@ -71,7 +69,6 @@ elseif (UNIX) CArchDaemonUnix.cpp CArchFileUnix.cpp CArchLogUnix.cpp - CArchIpcLogUnix.cpp CArchMultithreadPosix.cpp CArchNetworkBSD.cpp CArchSleepUnix.cpp diff --git a/src/lib/base/LogOutputters.cpp b/src/lib/base/LogOutputters.cpp index aeb5fd63..4d9fb787 100644 --- a/src/lib/base/LogOutputters.cpp +++ b/src/lib/base/LogOutputters.cpp @@ -262,41 +262,3 @@ CFileLogOutputter::close() {} void CFileLogOutputter::show(bool showIfEmpty) {} - - -// -// CConsoleLogOutputter -// - -CIpcLogOutputter::CIpcLogOutputter() -{ -} - -CIpcLogOutputter::~CIpcLogOutputter() -{ -} - -void -CIpcLogOutputter::open(const char* title) -{ - ARCH->ipcLog().openLog(title); -} - -void -CIpcLogOutputter::close() -{ - ARCH->ipcLog().closeLog(); -} - -void -CIpcLogOutputter::show(bool showIfEmpty) -{ - ARCH->ipcLog().showLog(showIfEmpty); -} - -bool -CIpcLogOutputter::write(ELevel level, const char* msg) -{ - ARCH->ipcLog().writeLog(level, msg); - return true; -} diff --git a/src/lib/base/LogOutputters.h b/src/lib/base/LogOutputters.h index 8fa26bac..24727be4 100644 --- a/src/lib/base/LogOutputters.h +++ b/src/lib/base/LogOutputters.h @@ -99,22 +99,6 @@ public: virtual bool write(ELevel level, const char* message); }; -//! Write log to GUI over IPC -/*! -This outputter writes output to the GUI via IPC. -*/ -class CIpcLogOutputter : public ILogOutputter { -public: - CIpcLogOutputter(); - virtual ~CIpcLogOutputter(); - - // ILogOutputter overrides - virtual void open(const char* title); - virtual void close(); - virtual void show(bool showIfEmpty); - virtual bool write(ELevel level, const char* message); -}; - //! Write log to system log only /*! Creating an object of this type inserts a CStopLogOutputter followed diff --git a/src/lib/ipc/CIpcClientProxy.cpp b/src/lib/ipc/CIpcClientProxy.cpp index 440e0b71..ca4967c4 100644 --- a/src/lib/ipc/CIpcClientProxy.cpp +++ b/src/lib/ipc/CIpcClientProxy.cpp @@ -25,7 +25,8 @@ CEvent::Type CIpcClientProxy::s_messageReceivedEvent = CEvent::kUnknown; CIpcClientProxy::CIpcClientProxy(IStream& stream) : -m_stream(stream) +m_stream(stream), +m_enableLog(false) { EVENTQUEUE->adoptHandler(m_stream.getInputReadyEvent(), stream.getEventTarget(), @@ -45,12 +46,16 @@ CIpcClientProxy::handleData(const CEvent&, void*) UInt8 code[1]; UInt32 n = m_stream.read(code, 1); while (n != 0) { + UInt8 type = code[0]; CIpcMessage* m = new CIpcMessage(); - m->m_type = code[1]; + m->m_type = type; - LOG((CLOG_DEBUG "ipc client proxy read: %d", code[0])); - switch (code[0]) { + if (m_enableLog) { + LOG((CLOG_DEBUG "ipc client proxy read: %d", code[0])); + } + + switch (type) { case kIpcCommand: m->m_data = parseCommand(); break; @@ -71,7 +76,9 @@ CIpcClientProxy::handleData(const CEvent&, void*) void CIpcClientProxy::send(const CIpcMessage& message) { - LOG((CLOG_DEBUG "ipc client proxy write: %d", message.m_type)); + if (m_enableLog) { + LOG((CLOG_DEBUG "ipc client proxy write: %d", message.m_type)); + } UInt8 code[1]; code[0] = message.m_type; @@ -80,17 +87,21 @@ CIpcClientProxy::send(const CIpcMessage& message) switch (message.m_type) { case kIpcLogLine: { CString* s = (CString*)message.m_data; + const char* data = s->c_str(); + int len = strlen(data); - UInt8 len[1]; - len[0] = s->size(); - m_stream.write(len, 1); + UInt8 lenBuf[1]; + lenBuf[0] = len; + m_stream.write(lenBuf, 1); - m_stream.write(s->c_str(), s->size()); + m_stream.write(data, len); } break; default: - LOG((CLOG_ERR "message not supported: %d", message.m_type)); + if (m_enableLog) { + LOG((CLOG_ERR "message not supported: %d", message.m_type)); + } break; } } @@ -110,7 +121,9 @@ CIpcClientProxy::parseCommand() void CIpcClientProxy::disconnect() { - LOG((CLOG_NOTE "disconnect, closing stream")); + if (m_enableLog) { + LOG((CLOG_NOTE "disconnect, closing stream")); + } m_stream.close(); } diff --git a/src/lib/ipc/CIpcClientProxy.h b/src/lib/ipc/CIpcClientProxy.h index 6797ea68..381c3849 100644 --- a/src/lib/ipc/CIpcClientProxy.h +++ b/src/lib/ipc/CIpcClientProxy.h @@ -40,6 +40,7 @@ private: public: IStream& m_stream; + bool m_enableLog; private: static CEvent::Type s_messageReceivedEvent; diff --git a/src/lib/arch/CArchIpcLogUnix.cpp b/src/lib/ipc/CIpcLogOutputter.cpp similarity index 57% rename from src/lib/arch/CArchIpcLogUnix.cpp rename to src/lib/ipc/CIpcLogOutputter.cpp index 14a44e0e..0a42af6a 100644 --- a/src/lib/arch/CArchIpcLogUnix.cpp +++ b/src/lib/ipc/CIpcLogOutputter.cpp @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,32 +15,41 @@ * along with this program. If not, see . */ -#include "CArchIpcLogUnix.h" +#include "CIpcLogOutputter.h" +#include "CIpcServer.h" +#include "CIpcMessage.h" +#include "Ipc.h" -CArchIpcLogUnix::CArchIpcLogUnix() +CIpcLogOutputter::CIpcLogOutputter(CIpcServer& ipcServer) : +m_ipcServer(ipcServer) { } -CArchIpcLogUnix::~CArchIpcLogUnix() +CIpcLogOutputter::~CIpcLogOutputter() { } void -CArchIpcLogUnix::openLog(const char* name) +CIpcLogOutputter::open(const char* title) { } void -CArchIpcLogUnix::closeLog() +CIpcLogOutputter::close() { } void -CArchIpcLogUnix::showLog(bool showIfEmpty) +CIpcLogOutputter::show(bool showIfEmpty) { } -void -CArchIpcLogUnix::writeLog(ELevel, const char*) +bool +CIpcLogOutputter::write(ELevel level, const char* msg) { + CIpcMessage message; + message.m_type = kIpcLogLine; + message.m_data = new CString(msg); + m_ipcServer.send(message); + return true; } diff --git a/src/lib/arch/CArchIpcLogUnix.h b/src/lib/ipc/CIpcLogOutputter.h similarity index 59% rename from src/lib/arch/CArchIpcLogUnix.h rename to src/lib/ipc/CIpcLogOutputter.h index 2f11d02e..34ca561c 100644 --- a/src/lib/arch/CArchIpcLogUnix.h +++ b/src/lib/ipc/CIpcLogOutputter.h @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,18 +17,25 @@ #pragma once -#include "IArchLog.h" +#include "ILogOutputter.h" -#define ARCH_IPC_LOG CArchIpcLogUnix +class CIpcServer; -class CArchIpcLogUnix : public IArchLog { +//! Write log to GUI over IPC +/*! +This outputter writes output to the GUI via IPC. +*/ +class CIpcLogOutputter : public ILogOutputter { public: - CArchIpcLogUnix(); - virtual ~CArchIpcLogUnix(); + CIpcLogOutputter(CIpcServer& ipcServer); + virtual ~CIpcLogOutputter(); - // IArchLog overrides - virtual void openLog(const char* name); - virtual void closeLog(); - virtual void showLog(bool showIfEmpty); - virtual void writeLog(ELevel, const char*); + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual void show(bool showIfEmpty); + virtual bool write(ELevel level, const char* message); + +private: + CIpcServer& m_ipcServer; }; diff --git a/src/lib/ipc/CIpcMessage.cpp b/src/lib/ipc/CIpcMessage.cpp index 198f0719..2376d90f 100644 --- a/src/lib/ipc/CIpcMessage.cpp +++ b/src/lib/ipc/CIpcMessage.cpp @@ -17,11 +17,15 @@ #include "CIpcMessage.h" -CIpcMessage::CIpcMessage() +CIpcMessage::CIpcMessage() : +m_type(0), +m_data(nullptr) { } CIpcMessage::~CIpcMessage() { - delete m_data; + if (m_data != nullptr) { + delete m_data; + } } diff --git a/src/lib/ipc/CIpcServer.cpp b/src/lib/ipc/CIpcServer.cpp index 383479ff..4fc66d22 100644 --- a/src/lib/ipc/CIpcServer.cpp +++ b/src/lib/ipc/CIpcServer.cpp @@ -57,13 +57,15 @@ CIpcServer::handleClientConnecting(const CEvent&, void*) if (stream == NULL) { return; } + // when there is already a client connected, this causes stack overflow, + // LOG((CLOG_NOTE "accepted ipc client connection")); // TODO: delete on disconnect CIpcClientProxy* proxy = new CIpcClientProxy(*stream); m_clients.insert(proxy); - EVENTQUEUE->addEvent(CEvent(getClientConnectedEvent(), this, proxy)); + EVENTQUEUE->addEvent(CEvent(getClientConnectedEvent(), this, proxy, CEvent::kDontFreeData)); } CEvent::Type diff --git a/src/lib/ipc/CMakeLists.txt b/src/lib/ipc/CMakeLists.txt index 44a7945d..7a1c4daf 100644 --- a/src/lib/ipc/CMakeLists.txt +++ b/src/lib/ipc/CMakeLists.txt @@ -20,6 +20,7 @@ set(inc CIpcServerProxy.h CIpcClientProxy.h CIpcMessage.h + CIpcLogOutputter.h ) set(src @@ -28,6 +29,7 @@ set(src CIpcServerProxy.cpp CIpcClientProxy.cpp CIpcMessage.cpp + CIpcLogOutputter.cpp ) if (WIN32) diff --git a/src/lib/platform/CMSWindowsRelauncher.cpp b/src/lib/platform/CMSWindowsRelauncher.cpp index 9827ec06..17e8a58f 100644 --- a/src/lib/platform/CMSWindowsRelauncher.cpp +++ b/src/lib/platform/CMSWindowsRelauncher.cpp @@ -25,6 +25,7 @@ #include "XArchWindows.h" #include "CApp.h" #include "CArgsBase.h" +#include "CIpcLogOutputter.h" #include #include @@ -42,7 +43,8 @@ CMSWindowsRelauncher::CMSWindowsRelauncher(bool autoDetectCommand) : m_running(true), m_commandChanged(false), m_stdOutWrite(NULL), - m_stdOutRead(NULL) + m_stdOutRead(NULL), + m_ipcLogOutputter(nullptr) { } @@ -403,7 +405,7 @@ CMSWindowsRelauncher::outputLoop(void*) else { // send process output over IPC to GUI. buffer[bytesRead] = '\0'; - ARCH->ipcLog().writeLog(kINFO, buffer); + m_ipcLogOutputter->write(kINFO, buffer); } } diff --git a/src/lib/platform/CMSWindowsRelauncher.h b/src/lib/platform/CMSWindowsRelauncher.h index 7b05f31a..c8c12a78 100644 --- a/src/lib/platform/CMSWindowsRelauncher.h +++ b/src/lib/platform/CMSWindowsRelauncher.h @@ -23,6 +23,7 @@ #include class CThread; +class CIpcLogOutputter; class CMSWindowsRelauncher { public: @@ -42,6 +43,9 @@ private: void sendIpcMessage(int type, const char* data); void shutdownProcess(const PROCESS_INFORMATION& pi, int timeout); +public: + CIpcLogOutputter* m_ipcLogOutputter; + private: CThread* m_thread; bool m_autoDetectCommand; diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 1fe7e17a..bb2bd414 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -137,6 +137,7 @@ set(inc ../common ../mt ../synergy + ../ipc ) if (UNIX) @@ -191,5 +192,5 @@ if (WIN32) endif() if (UNIX) - target_link_libraries(platform synergy ${libs}) + target_link_libraries(platform ipc synergy ${libs}) endif() diff --git a/src/lib/synergy/CDaemonApp.cpp b/src/lib/synergy/CDaemonApp.cpp index 23155423..a5f47911 100644 --- a/src/lib/synergy/CDaemonApp.cpp +++ b/src/lib/synergy/CDaemonApp.cpp @@ -38,6 +38,11 @@ #include "CMSWindowsRelauncher.h" #include "CMSWindowsDebugOutputter.h" #include "TMethodJob.h" +#include "TMethodEventJob.h" +#include "CIpcClientProxy.h" +#include "CIpcMessage.h" +#include "CSocketMultiplexer.h" +#include "CIpcLogOutputter.h" #define WIN32_LEAN_AND_MEAN #include @@ -69,9 +74,11 @@ winMainLoopStatic(int, const char**) } #endif -CDaemonApp::CDaemonApp() +CDaemonApp::CDaemonApp() : +m_ipcServer(nullptr), +m_ipcLogOutputter(nullptr) #if SYSAPI_WIN32 - : m_relauncher(false) +,m_relauncher(false) #endif { s_instance = this; @@ -91,15 +98,9 @@ CDaemonApp::run(int argc, char** argv) CArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); #endif - // send logging to gui via ipc - CLOG->insert(new CIpcLogOutputter()); - #if SYSAPI_WIN32 // sends debug messages to visual studio console window. CLOG->insert(new CMSWindowsDebugOutputter()); - - CThread pipeThread(new TMethodJob( - this, &CDaemonApp::pipeThread, nullptr)); #endif // default log level to system setting. @@ -174,6 +175,23 @@ CDaemonApp::mainLoop(bool logToFile) CEventQueue eventQueue; + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // uses event queue, must be created here. + m_ipcServer = new CIpcServer(); + + eventQueue.adoptHandler( + CIpcServer::getClientConnectedEvent(), m_ipcServer, + new TMethodEventJob(this, &CDaemonApp::handleIpcConnected)); + + m_ipcServer->listen(); + + // send logging to gui via ipc, log system adopts outputter. + m_ipcLogOutputter = new CIpcLogOutputter(*m_ipcServer); + CLOG->insert(m_ipcLogOutputter); + #if SYSAPI_WIN32 // HACK: create a dummy screen, which can handle system events // (such as a stop request from the service controller). @@ -181,6 +199,8 @@ CDaemonApp::mainLoop(bool logToFile) CGameDeviceInfo gameDevice; CScreen dummyScreen(new CMSWindowsScreen(false, true, gameDevice)); + m_relauncher.m_ipcLogOutputter = m_ipcLogOutputter; + string command = ARCH->setting("Command"); if (command != "") { LOG((CLOG_INFO "using last known command: %s", command.c_str())); @@ -190,22 +210,27 @@ CDaemonApp::mainLoop(bool logToFile) m_relauncher.startAsync(); #endif - EVENTQUEUE->loop(); + eventQueue.loop(); #if SYSAPI_WIN32 m_relauncher.stop(); #endif + eventQueue.removeHandler( + CIpcServer::getClientConnectedEvent(), m_ipcServer); + + delete m_ipcServer; + DAEMON_RUNNING(false); } catch (XArch& e) { - LOG((CLOG_ERR, e.what().c_str())); + LOG((CLOG_ERR "xarch exception: %s", e.what().c_str())); } catch (std::exception& e) { - LOG((CLOG_ERR, e.what())); + LOG((CLOG_ERR "std exception: %s", e.what())); } catch (...) { - LOG((CLOG_ERR, "Unrecognized error.")); + LOG((CLOG_ERR "unrecognized error.")); } } @@ -237,84 +262,45 @@ CDaemonApp::logPath() #endif } -#ifdef SYSAPI_WIN32 - void -CDaemonApp::pipeThread(void*) +CDaemonApp::handleIpcConnected(const CEvent& e, void*) { - // TODO: move this to an IPC server class. - while (true) { - - // grant access to everyone. - SECURITY_DESCRIPTOR sd; - InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, TRUE, static_cast(0), FALSE); - - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = &sd; - - HANDLE pipe = CreateNamedPipe( - _T("\\\\.\\pipe\\Synergy"), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - 1024, 1024, 0, &sa); - - if (pipe == INVALID_HANDLE_VALUE) - XArch("could not create named pipe."); - - LOG((CLOG_DEBUG "opened daemon pipe: %d", pipe)); - BOOL connectResult = ConnectNamedPipe(pipe, NULL); - - char buffer[1024]; - DWORD bytesRead; - - while (true) { - if (!ReadFile(pipe, buffer, sizeof(buffer), &bytesRead, NULL)) { - break; - } - - buffer[bytesRead] = '\0'; - LOG((CLOG_DEBUG "ipc daemon server read: %s", buffer)); - - try { - handlePipeMessage(buffer); - } - catch (XArch& ex) { - LOG((CLOG_ERR "handle message failed: %s", ex.what().c_str())); - } - } - - DisconnectNamedPipe(pipe); - CloseHandle(pipe); - } + LOG((CLOG_INFO "ipc client connected")); + EVENTQUEUE->adoptHandler( + CIpcClientProxy::getMessageReceivedEvent(), e.getData(), + new TMethodEventJob( + this, &CDaemonApp::handleIpcMessage)); } void -CDaemonApp::handlePipeMessage(char* buffer) +CDaemonApp::handleIpcMessage(const CEvent& e, void*) { - switch (buffer[0]) { - case kIpcCommand: - { - string command(++buffer); + CIpcMessage& m = *reinterpret_cast(e.getData()); + + LOG((CLOG_DEBUG "ipc message: %d", m.m_type)); + + switch (m.m_type) { + case kIpcCommand: { + CString& command = *reinterpret_cast(m.m_data); + + try { + // store command in system settings. this is used when the daemon + // next starts. + ARCH->setting("Command", command); + } + catch (XArch& e) { + //LOG((CLOG_ERR "failed to save setting: %s", e.what().c_str())); + } - // store command in system settings. this is used when the daemon - // next starts. - ARCH->setting("Command", command); - // tell the relauncher about the new command. this causes the // relauncher to stop the existing command and start the new // command. m_relauncher.command(command); } break; - - default: - LOG((CLOG_WARN "unrecognized ipc message: %d", buffer[0])); - break; + + default: + LOG((CLOG_ERR "ipc message not supported: %d", m.m_type)); + break; } } - - -#endif diff --git a/src/lib/synergy/CDaemonApp.h b/src/lib/synergy/CDaemonApp.h index a87d5d2d..2c9ecb8f 100644 --- a/src/lib/synergy/CDaemonApp.h +++ b/src/lib/synergy/CDaemonApp.h @@ -18,6 +18,7 @@ #pragma once #include "CArch.h" +#include "CIpcServer.h" #if SYSAPI_WIN32 #include "CMSWindowsRelauncher.h" @@ -25,6 +26,9 @@ #include +class CEvent; +class CIpcLogOutputter; + class CDaemonApp { public: @@ -37,16 +41,19 @@ private: void daemonize(); void foregroundError(const char* message); std::string logPath(); -#if SYSAPI_WIN32 - void pipeThread(void*); - void handlePipeMessage(char* buffer); -#endif + void handleIpcConnected(const CEvent&, void*); + void handleIpcMessage(const CEvent&, void*); public: static CDaemonApp* s_instance; + #if SYSAPI_WIN32 CMSWindowsRelauncher m_relauncher; #endif + +private: + CIpcServer* m_ipcServer; + CIpcLogOutputter* m_ipcLogOutputter; }; #define LOG_FILENAME "synergyd.log" diff --git a/src/lib/synergy/CMakeLists.txt b/src/lib/synergy/CMakeLists.txt index 1706c3b4..69b0dae9 100644 --- a/src/lib/synergy/CMakeLists.txt +++ b/src/lib/synergy/CMakeLists.txt @@ -102,6 +102,7 @@ set(inc ../client ../common ../io + ../ipc ../mt ../net ../platform @@ -125,5 +126,5 @@ include_directories(${inc}) add_library(synergy STATIC ${src}) if (UNIX) - target_link_libraries(synergy arch client net base platform mt server) + target_link_libraries(synergy arch client ipc net base platform mt server) endif() diff --git a/src/test/integtests/CIpcTests.cpp b/src/test/integtests/CIpcTests.cpp index 11c69011..8728a497 100644 --- a/src/test/integtests/CIpcTests.cpp +++ b/src/test/integtests/CIpcTests.cpp @@ -180,7 +180,7 @@ CIpcTests::sendMessageToClient_handleConnected(const CEvent& e, void*) void CIpcTests::sendMessageToClient_handleMessageReceived(const CEvent& e, void*) { - CIpcMessage* m = (CIpcMessage*)e.getData(); + CIpcMessage* m = reinterpret_cast(e.getData()); m_sendMessageToClient_receivedString = *((CString*)m->m_data); raiseQuitEvent(); }