fully replaced gui/daemon named pipes ipc with tcp ipc.

This commit is contained in:
Nick Bolton 2012-07-03 14:15:05 +00:00
parent 79d73bd163
commit 7d5fbde71d
25 changed files with 315 additions and 399 deletions

View File

@ -22,6 +22,7 @@ set(inc
../../lib/base ../../lib/base
../../lib/common ../../lib/common
../../lib/io ../../lib/io
../../lib/ipc
../../lib/mt ../../lib/mt
../../lib/net ../../lib/net
../../lib/platform ../../lib/platform
@ -47,7 +48,7 @@ if (VNC_SUPPORT)
endif() endif()
target_link_libraries(synergyd 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) if (CONF_CPACK)
install(TARGETS install(TARGETS

View File

@ -36,7 +36,8 @@ SOURCES += src/main.cpp \
src/QSynergyApplication.cpp \ src/QSynergyApplication.cpp \
src/VersionChecker.cpp \ src/VersionChecker.cpp \
src/SetupWizard.cpp \ src/SetupWizard.cpp \
src/IpcLogReader.cpp src/IpcLogReader.cpp \
src/IpcClient.cpp
HEADERS += src/MainWindow.h \ HEADERS += src/MainWindow.h \
src/AboutDialog.h \ src/AboutDialog.h \
src/ServerConfig.h \ src/ServerConfig.h \
@ -59,7 +60,8 @@ HEADERS += src/MainWindow.h \
src/QSynergyApplication.h \ src/QSynergyApplication.h \
src/VersionChecker.h \ src/VersionChecker.h \
src/SetupWizard.h \ src/SetupWizard.h \
src/IpcLogReader.h src/IpcLogReader.h \
src/IpcClient.h
RESOURCES += res/Synergy.qrc RESOURCES += res/Synergy.qrc
RC_FILE = res/win/Synergy.rc RC_FILE = res/win/Synergy.rc
TRANSLATIONS = res/lang/nl_NL.ts TRANSLATIONS = res/lang/nl_NL.ts

83
src/gui/src/IpcClient.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "IpcClient.h"
#include <QTcpSocket>
#include <QHostAddress>
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;
}
}

View File

@ -1,48 +1,53 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton * Copyright (C) 2012 Nick Bolton
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file. * found in the file COPYING that should have accompanied this file.
* *
* This package is distributed in the hope that it will be useful, * This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#define WIN32_LEAN_AND_MEAN #include <QObject>
#include <QAbstractSocket>
#include "IArchLog.h"
#include <windows.h> #define IPC_PORT 24801
#define ARCH_IPC_LOG CArchIpcLogWindows class QTcpSocket;
class CThread; class IpcClient : public QObject
{
//! Win32 implementation of IArchLog (IPC version) Q_OBJECT
class CArchIpcLogWindows : public IArchLog {
public: public:
CArchIpcLogWindows(); IpcClient();
virtual ~CArchIpcLogWindows(); virtual ~IpcClient();
// IArchLog overrides void connectToHost();
virtual void openLog(const char* name); void write(unsigned char code, unsigned char length, const char* data);
virtual void closeLog();
virtual void showLog(bool showIfEmpty); private slots:
virtual void writeLog(ELevel, const char*); void read();
void error(QAbstractSocket::SocketError error);
private:
void connectThread(void*); signals:
void readLogLine(const QString& text);
private: void errorMessage(const QString& text);
HANDLE m_pipe;
CThread* m_listenThread; private:
bool m_connected; QTcpSocket* m_Socket;
}; };
enum EIpcMessage {
kIpcLogLine,
kIpcCommand
};

View File

@ -17,6 +17,8 @@
#define WEBSITE_ADDRESS "synergy-foss.org" #define WEBSITE_ADDRESS "synergy-foss.org"
#include <iostream>
#include "MainWindow.h" #include "MainWindow.h"
#include "AboutDialog.h" #include "AboutDialog.h"
#include "ServerConfigDialog.h" #include "ServerConfigDialog.h"
@ -88,8 +90,10 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
if (appConfig.processMode() == Service) if (appConfig.processMode() == Service)
{ {
connect(&m_IpcLogReader, SIGNAL(receivedLine(const QString&)), this, SLOT(appendLog(const QString&))); connect(&m_IpcClient, SIGNAL(readLogLine(const QString&)), this, SLOT(appendLog(const QString&)));
m_IpcLogReader.start(); 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) void MainWindow::sendDaemonCommand(const QString& command, bool showErrors)
{ {
sendIpcMessage(Command, command.toStdString().c_str(), showErrors); std::string s = command.toStdString();
} const char* data = s.c_str();
m_IpcClient.write(Command, strlen(data), data);
// 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
} }
void MainWindow::on_m_pActionWizard_triggered() void MainWindow::on_m_pActionWizard_triggered()

View File

@ -30,7 +30,7 @@
#include "ServerConfig.h" #include "ServerConfig.h"
#include "AppConfig.h" #include "AppConfig.h"
#include "VersionChecker.h" #include "VersionChecker.h"
#include "IpcLogReader.h" #include "IpcClient.h"
class QAction; class QAction;
class QMenu; class QMenu;
@ -142,7 +142,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
bool m_alreadyHidden; bool m_alreadyHidden;
VersionChecker m_versionChecker; VersionChecker m_versionChecker;
SetupWizard* m_SetupWizard; SetupWizard* m_SetupWizard;
IpcLogReader m_IpcLogReader; IpcClient m_IpcClient;
}; };
#endif #endif

View File

@ -42,7 +42,6 @@
# include "CArchDaemonWindows.h" # include "CArchDaemonWindows.h"
# include "CArchFileWindows.h" # include "CArchFileWindows.h"
# include "CArchLogWindows.h" # include "CArchLogWindows.h"
# include "CArchIpcLogWindows.h"
# include "CArchMiscWindows.h" # include "CArchMiscWindows.h"
# include "CArchMultithreadWindows.h" # include "CArchMultithreadWindows.h"
# include "CArchNetworkWinsock.h" # include "CArchNetworkWinsock.h"
@ -57,7 +56,6 @@
# include "CArchDaemonUnix.h" # include "CArchDaemonUnix.h"
# include "CArchFileUnix.h" # include "CArchFileUnix.h"
# include "CArchLogUnix.h" # include "CArchLogUnix.h"
# include "CArchIpcLogUnix.h"
# if HAVE_PTHREAD # if HAVE_PTHREAD
# include "CArchMultithreadPosix.h" # include "CArchMultithreadPosix.h"
# endif # endif
@ -111,7 +109,6 @@ public:
*/ */
static CArch* getInstance(); static CArch* getInstance();
ARCH_IPC_LOG& ipcLog() const { return (ARCH_IPC_LOG&)m_ipcLog; }
ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; } ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; }
private: private:
@ -120,7 +117,6 @@ private:
private: private:
static CArch* s_instance; static CArch* s_instance;
ARCH_IPC_LOG m_ipcLog;
ARCH_PLUGIN m_plugin; ARCH_PLUGIN m_plugin;
}; };

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "CArchIpcLogWindows.h"
#include "CArchMiscWindows.h"
#include "XArch.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CArch.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
//
// 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<PACL>(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<CArchIpcLogWindows>(
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);
}

View File

@ -27,7 +27,6 @@ if (WIN32)
CArchDaemonWindows.h CArchDaemonWindows.h
CArchFileWindows.h CArchFileWindows.h
CArchLogWindows.h CArchLogWindows.h
CArchIpcLogWindows.h
CArchMiscWindows.h CArchMiscWindows.h
CArchMultithreadWindows.h CArchMultithreadWindows.h
CArchNetworkWinsock.h CArchNetworkWinsock.h
@ -51,7 +50,6 @@ if (WIN32)
CArchDaemonWindows.cpp CArchDaemonWindows.cpp
CArchFileWindows.cpp CArchFileWindows.cpp
CArchLogWindows.cpp CArchLogWindows.cpp
CArchIpcLogWindows.cpp
CArchMiscWindows.cpp CArchMiscWindows.cpp
CArchMultithreadWindows.cpp CArchMultithreadWindows.cpp
CArchNetworkWinsock.cpp CArchNetworkWinsock.cpp
@ -71,7 +69,6 @@ elseif (UNIX)
CArchDaemonUnix.cpp CArchDaemonUnix.cpp
CArchFileUnix.cpp CArchFileUnix.cpp
CArchLogUnix.cpp CArchLogUnix.cpp
CArchIpcLogUnix.cpp
CArchMultithreadPosix.cpp CArchMultithreadPosix.cpp
CArchNetworkBSD.cpp CArchNetworkBSD.cpp
CArchSleepUnix.cpp CArchSleepUnix.cpp

View File

@ -262,41 +262,3 @@ CFileLogOutputter::close() {}
void void
CFileLogOutputter::show(bool showIfEmpty) {} 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;
}

View File

@ -99,22 +99,6 @@ public:
virtual bool write(ELevel level, const char* message); 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 //! Write log to system log only
/*! /*!
Creating an object of this type inserts a CStopLogOutputter followed Creating an object of this type inserts a CStopLogOutputter followed

View File

@ -25,7 +25,8 @@
CEvent::Type CIpcClientProxy::s_messageReceivedEvent = CEvent::kUnknown; CEvent::Type CIpcClientProxy::s_messageReceivedEvent = CEvent::kUnknown;
CIpcClientProxy::CIpcClientProxy(IStream& stream) : CIpcClientProxy::CIpcClientProxy(IStream& stream) :
m_stream(stream) m_stream(stream),
m_enableLog(false)
{ {
EVENTQUEUE->adoptHandler(m_stream.getInputReadyEvent(), EVENTQUEUE->adoptHandler(m_stream.getInputReadyEvent(),
stream.getEventTarget(), stream.getEventTarget(),
@ -45,12 +46,16 @@ CIpcClientProxy::handleData(const CEvent&, void*)
UInt8 code[1]; UInt8 code[1];
UInt32 n = m_stream.read(code, 1); UInt32 n = m_stream.read(code, 1);
while (n != 0) { while (n != 0) {
UInt8 type = code[0];
CIpcMessage* m = new CIpcMessage(); CIpcMessage* m = new CIpcMessage();
m->m_type = code[1]; m->m_type = type;
LOG((CLOG_DEBUG "ipc client proxy read: %d", code[0])); if (m_enableLog) {
switch (code[0]) { LOG((CLOG_DEBUG "ipc client proxy read: %d", code[0]));
}
switch (type) {
case kIpcCommand: case kIpcCommand:
m->m_data = parseCommand(); m->m_data = parseCommand();
break; break;
@ -71,7 +76,9 @@ CIpcClientProxy::handleData(const CEvent&, void*)
void void
CIpcClientProxy::send(const CIpcMessage& message) 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]; UInt8 code[1];
code[0] = message.m_type; code[0] = message.m_type;
@ -80,17 +87,21 @@ CIpcClientProxy::send(const CIpcMessage& message)
switch (message.m_type) { switch (message.m_type) {
case kIpcLogLine: { case kIpcLogLine: {
CString* s = (CString*)message.m_data; CString* s = (CString*)message.m_data;
const char* data = s->c_str();
int len = strlen(data);
UInt8 len[1]; UInt8 lenBuf[1];
len[0] = s->size(); lenBuf[0] = len;
m_stream.write(len, 1); m_stream.write(lenBuf, 1);
m_stream.write(s->c_str(), s->size()); m_stream.write(data, len);
} }
break; break;
default: 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; break;
} }
} }
@ -110,7 +121,9 @@ CIpcClientProxy::parseCommand()
void void
CIpcClientProxy::disconnect() CIpcClientProxy::disconnect()
{ {
LOG((CLOG_NOTE "disconnect, closing stream")); if (m_enableLog) {
LOG((CLOG_NOTE "disconnect, closing stream"));
}
m_stream.close(); m_stream.close();
} }

View File

@ -40,6 +40,7 @@ private:
public: public:
IStream& m_stream; IStream& m_stream;
bool m_enableLog;
private: private:
static CEvent::Type s_messageReceivedEvent; static CEvent::Type s_messageReceivedEvent;

View File

@ -1,11 +1,11 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton * Copyright (C) 2012 Nick Bolton
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file. * found in the file COPYING that should have accompanied this file.
* *
* This package is distributed in the hope that it will be useful, * This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@ -15,32 +15,41 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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 void
CArchIpcLogUnix::openLog(const char* name) CIpcLogOutputter::open(const char* title)
{ {
} }
void void
CArchIpcLogUnix::closeLog() CIpcLogOutputter::close()
{ {
} }
void void
CArchIpcLogUnix::showLog(bool showIfEmpty) CIpcLogOutputter::show(bool showIfEmpty)
{ {
} }
void bool
CArchIpcLogUnix::writeLog(ELevel, const char*) 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;
} }

View File

@ -1,11 +1,11 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton * Copyright (C) 2012 Nick Bolton
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file. * found in the file COPYING that should have accompanied this file.
* *
* This package is distributed in the hope that it will be useful, * This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@ -17,18 +17,25 @@
#pragma once #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: public:
CArchIpcLogUnix(); CIpcLogOutputter(CIpcServer& ipcServer);
virtual ~CArchIpcLogUnix(); virtual ~CIpcLogOutputter();
// IArchLog overrides // ILogOutputter overrides
virtual void openLog(const char* name); virtual void open(const char* title);
virtual void closeLog(); virtual void close();
virtual void showLog(bool showIfEmpty); virtual void show(bool showIfEmpty);
virtual void writeLog(ELevel, const char*); virtual bool write(ELevel level, const char* message);
private:
CIpcServer& m_ipcServer;
}; };

View File

@ -17,11 +17,15 @@
#include "CIpcMessage.h" #include "CIpcMessage.h"
CIpcMessage::CIpcMessage() CIpcMessage::CIpcMessage() :
m_type(0),
m_data(nullptr)
{ {
} }
CIpcMessage::~CIpcMessage() CIpcMessage::~CIpcMessage()
{ {
delete m_data; if (m_data != nullptr) {
delete m_data;
}
} }

View File

@ -57,13 +57,15 @@ CIpcServer::handleClientConnecting(const CEvent&, void*)
if (stream == NULL) { if (stream == NULL) {
return; return;
} }
// when there is already a client connected, this causes stack overflow,
//
LOG((CLOG_NOTE "accepted ipc client connection")); LOG((CLOG_NOTE "accepted ipc client connection"));
// TODO: delete on disconnect // TODO: delete on disconnect
CIpcClientProxy* proxy = new CIpcClientProxy(*stream); CIpcClientProxy* proxy = new CIpcClientProxy(*stream);
m_clients.insert(proxy); m_clients.insert(proxy);
EVENTQUEUE->addEvent(CEvent(getClientConnectedEvent(), this, proxy)); EVENTQUEUE->addEvent(CEvent(getClientConnectedEvent(), this, proxy, CEvent::kDontFreeData));
} }
CEvent::Type CEvent::Type

View File

@ -20,6 +20,7 @@ set(inc
CIpcServerProxy.h CIpcServerProxy.h
CIpcClientProxy.h CIpcClientProxy.h
CIpcMessage.h CIpcMessage.h
CIpcLogOutputter.h
) )
set(src set(src
@ -28,6 +29,7 @@ set(src
CIpcServerProxy.cpp CIpcServerProxy.cpp
CIpcClientProxy.cpp CIpcClientProxy.cpp
CIpcMessage.cpp CIpcMessage.cpp
CIpcLogOutputter.cpp
) )
if (WIN32) if (WIN32)

View File

@ -25,6 +25,7 @@
#include "XArchWindows.h" #include "XArchWindows.h"
#include "CApp.h" #include "CApp.h"
#include "CArgsBase.h" #include "CArgsBase.h"
#include "CIpcLogOutputter.h"
#include <Tlhelp32.h> #include <Tlhelp32.h>
#include <UserEnv.h> #include <UserEnv.h>
@ -42,7 +43,8 @@ CMSWindowsRelauncher::CMSWindowsRelauncher(bool autoDetectCommand) :
m_running(true), m_running(true),
m_commandChanged(false), m_commandChanged(false),
m_stdOutWrite(NULL), m_stdOutWrite(NULL),
m_stdOutRead(NULL) m_stdOutRead(NULL),
m_ipcLogOutputter(nullptr)
{ {
} }
@ -403,7 +405,7 @@ CMSWindowsRelauncher::outputLoop(void*)
else { else {
// send process output over IPC to GUI. // send process output over IPC to GUI.
buffer[bytesRead] = '\0'; buffer[bytesRead] = '\0';
ARCH->ipcLog().writeLog(kINFO, buffer); m_ipcLogOutputter->write(kINFO, buffer);
} }
} }

View File

@ -23,6 +23,7 @@
#include <list> #include <list>
class CThread; class CThread;
class CIpcLogOutputter;
class CMSWindowsRelauncher { class CMSWindowsRelauncher {
public: public:
@ -42,6 +43,9 @@ private:
void sendIpcMessage(int type, const char* data); void sendIpcMessage(int type, const char* data);
void shutdownProcess(const PROCESS_INFORMATION& pi, int timeout); void shutdownProcess(const PROCESS_INFORMATION& pi, int timeout);
public:
CIpcLogOutputter* m_ipcLogOutputter;
private: private:
CThread* m_thread; CThread* m_thread;
bool m_autoDetectCommand; bool m_autoDetectCommand;

View File

@ -137,6 +137,7 @@ set(inc
../common ../common
../mt ../mt
../synergy ../synergy
../ipc
) )
if (UNIX) if (UNIX)
@ -191,5 +192,5 @@ if (WIN32)
endif() endif()
if (UNIX) if (UNIX)
target_link_libraries(platform synergy ${libs}) target_link_libraries(platform ipc synergy ${libs})
endif() endif()

View File

@ -38,6 +38,11 @@
#include "CMSWindowsRelauncher.h" #include "CMSWindowsRelauncher.h"
#include "CMSWindowsDebugOutputter.h" #include "CMSWindowsDebugOutputter.h"
#include "TMethodJob.h" #include "TMethodJob.h"
#include "TMethodEventJob.h"
#include "CIpcClientProxy.h"
#include "CIpcMessage.h"
#include "CSocketMultiplexer.h"
#include "CIpcLogOutputter.h"
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
@ -69,9 +74,11 @@ winMainLoopStatic(int, const char**)
} }
#endif #endif
CDaemonApp::CDaemonApp() CDaemonApp::CDaemonApp() :
m_ipcServer(nullptr),
m_ipcLogOutputter(nullptr)
#if SYSAPI_WIN32 #if SYSAPI_WIN32
: m_relauncher(false) ,m_relauncher(false)
#endif #endif
{ {
s_instance = this; s_instance = this;
@ -91,15 +98,9 @@ CDaemonApp::run(int argc, char** argv)
CArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); CArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
#endif #endif
// send logging to gui via ipc
CLOG->insert(new CIpcLogOutputter());
#if SYSAPI_WIN32 #if SYSAPI_WIN32
// sends debug messages to visual studio console window. // sends debug messages to visual studio console window.
CLOG->insert(new CMSWindowsDebugOutputter()); CLOG->insert(new CMSWindowsDebugOutputter());
CThread pipeThread(new TMethodJob<CDaemonApp>(
this, &CDaemonApp::pipeThread, nullptr));
#endif #endif
// default log level to system setting. // default log level to system setting.
@ -174,6 +175,23 @@ CDaemonApp::mainLoop(bool logToFile)
CEventQueue eventQueue; 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<CDaemonApp>(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 #if SYSAPI_WIN32
// HACK: create a dummy screen, which can handle system events // HACK: create a dummy screen, which can handle system events
// (such as a stop request from the service controller). // (such as a stop request from the service controller).
@ -181,6 +199,8 @@ CDaemonApp::mainLoop(bool logToFile)
CGameDeviceInfo gameDevice; CGameDeviceInfo gameDevice;
CScreen dummyScreen(new CMSWindowsScreen(false, true, gameDevice)); CScreen dummyScreen(new CMSWindowsScreen(false, true, gameDevice));
m_relauncher.m_ipcLogOutputter = m_ipcLogOutputter;
string command = ARCH->setting("Command"); string command = ARCH->setting("Command");
if (command != "") { if (command != "") {
LOG((CLOG_INFO "using last known command: %s", command.c_str())); LOG((CLOG_INFO "using last known command: %s", command.c_str()));
@ -190,22 +210,27 @@ CDaemonApp::mainLoop(bool logToFile)
m_relauncher.startAsync(); m_relauncher.startAsync();
#endif #endif
EVENTQUEUE->loop(); eventQueue.loop();
#if SYSAPI_WIN32 #if SYSAPI_WIN32
m_relauncher.stop(); m_relauncher.stop();
#endif #endif
eventQueue.removeHandler(
CIpcServer::getClientConnectedEvent(), m_ipcServer);
delete m_ipcServer;
DAEMON_RUNNING(false); DAEMON_RUNNING(false);
} }
catch (XArch& e) { catch (XArch& e) {
LOG((CLOG_ERR, e.what().c_str())); LOG((CLOG_ERR "xarch exception: %s", e.what().c_str()));
} }
catch (std::exception& e) { catch (std::exception& e) {
LOG((CLOG_ERR, e.what())); LOG((CLOG_ERR "std exception: %s", e.what()));
} }
catch (...) { catch (...) {
LOG((CLOG_ERR, "Unrecognized error.")); LOG((CLOG_ERR "unrecognized error."));
} }
} }
@ -237,84 +262,45 @@ CDaemonApp::logPath()
#endif #endif
} }
#ifdef SYSAPI_WIN32
void void
CDaemonApp::pipeThread(void*) CDaemonApp::handleIpcConnected(const CEvent& e, void*)
{ {
// TODO: move this to an IPC server class. LOG((CLOG_INFO "ipc client connected"));
while (true) { EVENTQUEUE->adoptHandler(
CIpcClientProxy::getMessageReceivedEvent(), e.getData(),
// grant access to everyone. new TMethodEventJob<CDaemonApp>(
SECURITY_DESCRIPTOR sd; this, &CDaemonApp::handleIpcMessage));
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(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);
}
} }
void void
CDaemonApp::handlePipeMessage(char* buffer) CDaemonApp::handleIpcMessage(const CEvent& e, void*)
{ {
switch (buffer[0]) { CIpcMessage& m = *reinterpret_cast<CIpcMessage*>(e.getData());
case kIpcCommand:
{ LOG((CLOG_DEBUG "ipc message: %d", m.m_type));
string command(++buffer);
switch (m.m_type) {
case kIpcCommand: {
CString& command = *reinterpret_cast<CString*>(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 // tell the relauncher about the new command. this causes the
// relauncher to stop the existing command and start the new // relauncher to stop the existing command and start the new
// command. // command.
m_relauncher.command(command); m_relauncher.command(command);
} }
break; break;
default: default:
LOG((CLOG_WARN "unrecognized ipc message: %d", buffer[0])); LOG((CLOG_ERR "ipc message not supported: %d", m.m_type));
break; break;
} }
} }
#endif

View File

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "CArch.h" #include "CArch.h"
#include "CIpcServer.h"
#if SYSAPI_WIN32 #if SYSAPI_WIN32
#include "CMSWindowsRelauncher.h" #include "CMSWindowsRelauncher.h"
@ -25,6 +26,9 @@
#include <string> #include <string>
class CEvent;
class CIpcLogOutputter;
class CDaemonApp { class CDaemonApp {
public: public:
@ -37,16 +41,19 @@ private:
void daemonize(); void daemonize();
void foregroundError(const char* message); void foregroundError(const char* message);
std::string logPath(); std::string logPath();
#if SYSAPI_WIN32 void handleIpcConnected(const CEvent&, void*);
void pipeThread(void*); void handleIpcMessage(const CEvent&, void*);
void handlePipeMessage(char* buffer);
#endif
public: public:
static CDaemonApp* s_instance; static CDaemonApp* s_instance;
#if SYSAPI_WIN32 #if SYSAPI_WIN32
CMSWindowsRelauncher m_relauncher; CMSWindowsRelauncher m_relauncher;
#endif #endif
private:
CIpcServer* m_ipcServer;
CIpcLogOutputter* m_ipcLogOutputter;
}; };
#define LOG_FILENAME "synergyd.log" #define LOG_FILENAME "synergyd.log"

View File

@ -102,6 +102,7 @@ set(inc
../client ../client
../common ../common
../io ../io
../ipc
../mt ../mt
../net ../net
../platform ../platform
@ -125,5 +126,5 @@ include_directories(${inc})
add_library(synergy STATIC ${src}) add_library(synergy STATIC ${src})
if (UNIX) 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() endif()

View File

@ -180,7 +180,7 @@ CIpcTests::sendMessageToClient_handleConnected(const CEvent& e, void*)
void void
CIpcTests::sendMessageToClient_handleMessageReceived(const CEvent& e, void*) CIpcTests::sendMessageToClient_handleMessageReceived(const CEvent& e, void*)
{ {
CIpcMessage* m = (CIpcMessage*)e.getData(); CIpcMessage* m = reinterpret_cast<CIpcMessage*>(e.getData());
m_sendMessageToClient_receivedString = *((CString*)m->m_data); m_sendMessageToClient_receivedString = *((CString*)m->m_data);
raiseQuitEvent(); raiseQuitEvent();
} }