Remove IPC
This commit is contained in:
parent
607201d586
commit
5ff0637a82
|
@ -322,6 +322,6 @@ add_subdirectory (src)
|
||||||
|
|
||||||
if (SYNERGY_TIDY)
|
if (SYNERGY_TIDY)
|
||||||
set_property (TARGET synergys synergyc synergy-core
|
set_property (TARGET synergys synergyc synergy-core
|
||||||
arch base client common core io ipc mt net platform server shared
|
arch base client common core io mt net platform server shared
|
||||||
PROPERTY CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
|
PROPERTY CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
add_executable(synergy-core main.cpp)
|
add_executable(synergy-core main.cpp)
|
||||||
target_link_libraries(synergy-core
|
target_link_libraries(synergy-core
|
||||||
arch base client common io mt net ipc platform server core ${libs})
|
arch base client common io mt net platform server core ${libs})
|
||||||
|
|
||||||
if (SYNERGY_CORE_INSTALL)
|
if (SYNERGY_CORE_INSTALL)
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
|
|
@ -39,4 +39,4 @@ endif()
|
||||||
|
|
||||||
add_executable(synergyc ${sources})
|
add_executable(synergyc ${sources})
|
||||||
target_link_libraries(synergyc
|
target_link_libraries(synergyc
|
||||||
arch base client common io mt net ipc platform server core ${libs})
|
arch base client common io mt net platform server core ${libs})
|
||||||
|
|
|
@ -36,4 +36,4 @@ endif()
|
||||||
|
|
||||||
add_executable(synergys ${sources})
|
add_executable(synergys ${sources})
|
||||||
target_link_libraries(synergys
|
target_link_libraries(synergys
|
||||||
arch base client common io mt net ipc platform server core ${libs})
|
arch base client common io mt net platform server core ${libs})
|
||||||
|
|
|
@ -20,7 +20,6 @@ add_subdirectory(base)
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
add_subdirectory(io)
|
add_subdirectory(io)
|
||||||
add_subdirectory(ipc)
|
|
||||||
add_subdirectory(mt)
|
add_subdirectory(mt)
|
||||||
add_subdirectory(net)
|
add_subdirectory(net)
|
||||||
add_subdirectory(platform)
|
add_subdirectory(platform)
|
||||||
|
|
|
@ -30,10 +30,6 @@
|
||||||
|
|
||||||
EVENT_TYPE_ACCESSOR(Client)
|
EVENT_TYPE_ACCESSOR(Client)
|
||||||
EVENT_TYPE_ACCESSOR(IStream)
|
EVENT_TYPE_ACCESSOR(IStream)
|
||||||
EVENT_TYPE_ACCESSOR(IpcClient)
|
|
||||||
EVENT_TYPE_ACCESSOR(IpcClientProxy)
|
|
||||||
EVENT_TYPE_ACCESSOR(IpcServer)
|
|
||||||
EVENT_TYPE_ACCESSOR(IpcServerProxy)
|
|
||||||
EVENT_TYPE_ACCESSOR(IDataSocket)
|
EVENT_TYPE_ACCESSOR(IDataSocket)
|
||||||
EVENT_TYPE_ACCESSOR(IListenSocket)
|
EVENT_TYPE_ACCESSOR(IListenSocket)
|
||||||
EVENT_TYPE_ACCESSOR(ISocket)
|
EVENT_TYPE_ACCESSOR(ISocket)
|
||||||
|
@ -68,10 +64,6 @@ EventQueue::EventQueue() :
|
||||||
m_nextType(Event::kLast),
|
m_nextType(Event::kLast),
|
||||||
m_typesForClient(nullptr),
|
m_typesForClient(nullptr),
|
||||||
m_typesForIStream(nullptr),
|
m_typesForIStream(nullptr),
|
||||||
m_typesForIpcClient(nullptr),
|
|
||||||
m_typesForIpcClientProxy(nullptr),
|
|
||||||
m_typesForIpcServer(nullptr),
|
|
||||||
m_typesForIpcServerProxy(nullptr),
|
|
||||||
m_typesForIDataSocket(nullptr),
|
m_typesForIDataSocket(nullptr),
|
||||||
m_typesForIListenSocket(nullptr),
|
m_typesForIListenSocket(nullptr),
|
||||||
m_typesForISocket(nullptr),
|
m_typesForISocket(nullptr),
|
||||||
|
|
|
@ -141,10 +141,6 @@ public:
|
||||||
//
|
//
|
||||||
ClientEvents& forClient();
|
ClientEvents& forClient();
|
||||||
IStreamEvents& forIStream();
|
IStreamEvents& forIStream();
|
||||||
IpcClientEvents& forIpcClient();
|
|
||||||
IpcClientProxyEvents& forIpcClientProxy();
|
|
||||||
IpcServerEvents& forIpcServer();
|
|
||||||
IpcServerProxyEvents& forIpcServerProxy();
|
|
||||||
IDataSocketEvents& forIDataSocket();
|
IDataSocketEvents& forIDataSocket();
|
||||||
IListenSocketEvents& forIListenSocket();
|
IListenSocketEvents& forIListenSocket();
|
||||||
ISocketEvents& forISocket();
|
ISocketEvents& forISocket();
|
||||||
|
@ -163,10 +159,6 @@ public:
|
||||||
private:
|
private:
|
||||||
ClientEvents* m_typesForClient;
|
ClientEvents* m_typesForClient;
|
||||||
IStreamEvents* m_typesForIStream;
|
IStreamEvents* m_typesForIStream;
|
||||||
IpcClientEvents* m_typesForIpcClient;
|
|
||||||
IpcClientProxyEvents* m_typesForIpcClientProxy;
|
|
||||||
IpcServerEvents* m_typesForIpcServer;
|
|
||||||
IpcServerProxyEvents* m_typesForIpcServerProxy;
|
|
||||||
IDataSocketEvents* m_typesForIDataSocket;
|
IDataSocketEvents* m_typesForIDataSocket;
|
||||||
IListenSocketEvents* m_typesForIListenSocket;
|
IListenSocketEvents* m_typesForIListenSocket;
|
||||||
ISocketEvents* m_typesForISocket;
|
ISocketEvents* m_typesForISocket;
|
||||||
|
|
|
@ -57,26 +57,6 @@ REGISTER_EVENT(IStream, outputError)
|
||||||
REGISTER_EVENT(IStream, inputShutdown)
|
REGISTER_EVENT(IStream, inputShutdown)
|
||||||
REGISTER_EVENT(IStream, outputShutdown)
|
REGISTER_EVENT(IStream, outputShutdown)
|
||||||
|
|
||||||
//
|
|
||||||
// IpcClient
|
|
||||||
//
|
|
||||||
|
|
||||||
REGISTER_EVENT(IpcClient, connected)
|
|
||||||
REGISTER_EVENT(IpcClient, messageReceived)
|
|
||||||
|
|
||||||
//
|
|
||||||
// IpcClientProxy
|
|
||||||
//
|
|
||||||
|
|
||||||
REGISTER_EVENT(IpcClientProxy, messageReceived)
|
|
||||||
REGISTER_EVENT(IpcClientProxy, disconnected)
|
|
||||||
|
|
||||||
//
|
|
||||||
// IpcServerProxy
|
|
||||||
//
|
|
||||||
|
|
||||||
REGISTER_EVENT(IpcServerProxy, messageReceived)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// IDataSocket
|
// IDataSocket
|
||||||
//
|
//
|
||||||
|
@ -178,13 +158,6 @@ REGISTER_EVENT(IScreen, shapeChanged)
|
||||||
REGISTER_EVENT(IScreen, suspend)
|
REGISTER_EVENT(IScreen, suspend)
|
||||||
REGISTER_EVENT(IScreen, resume)
|
REGISTER_EVENT(IScreen, resume)
|
||||||
|
|
||||||
//
|
|
||||||
// IpcServer
|
|
||||||
//
|
|
||||||
|
|
||||||
REGISTER_EVENT(IpcServer, clientConnected)
|
|
||||||
REGISTER_EVENT(IpcServer, messageReceived)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Clipboard
|
// Clipboard
|
||||||
//
|
//
|
||||||
|
|
|
@ -143,89 +143,6 @@ private:
|
||||||
Event::Type m_outputShutdown;
|
Event::Type m_outputShutdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IpcClientEvents : public EventTypes {
|
|
||||||
public:
|
|
||||||
IpcClientEvents() :
|
|
||||||
m_connected(Event::kUnknown),
|
|
||||||
m_messageReceived(Event::kUnknown) { }
|
|
||||||
|
|
||||||
//! @name accessors
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Raised when the socket is connected.
|
|
||||||
Event::Type connected();
|
|
||||||
|
|
||||||
//! Raised when a message is received.
|
|
||||||
Event::Type messageReceived();
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Event::Type m_connected;
|
|
||||||
Event::Type m_messageReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IpcClientProxyEvents : public EventTypes {
|
|
||||||
public:
|
|
||||||
IpcClientProxyEvents() :
|
|
||||||
m_messageReceived(Event::kUnknown),
|
|
||||||
m_disconnected(Event::kUnknown) { }
|
|
||||||
|
|
||||||
//! @name accessors
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Raised when the server receives a message from a client.
|
|
||||||
Event::Type messageReceived();
|
|
||||||
|
|
||||||
//! Raised when the client disconnects from the server.
|
|
||||||
Event::Type disconnected();
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Event::Type m_messageReceived;
|
|
||||||
Event::Type m_disconnected;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IpcServerEvents : public EventTypes {
|
|
||||||
public:
|
|
||||||
IpcServerEvents() :
|
|
||||||
m_clientConnected(Event::kUnknown),
|
|
||||||
m_messageReceived(Event::kUnknown) { }
|
|
||||||
|
|
||||||
//! @name accessors
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Raised when we have created the client proxy.
|
|
||||||
Event::Type clientConnected();
|
|
||||||
|
|
||||||
//! Raised when a message is received through a client proxy.
|
|
||||||
Event::Type messageReceived();
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Event::Type m_clientConnected;
|
|
||||||
Event::Type m_messageReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IpcServerProxyEvents : public EventTypes {
|
|
||||||
public:
|
|
||||||
IpcServerProxyEvents() :
|
|
||||||
m_messageReceived(Event::kUnknown) { }
|
|
||||||
|
|
||||||
//! @name accessors
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Raised when the client receives a message from the server.
|
|
||||||
Event::Type messageReceived();
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Event::Type m_messageReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IDataSocketEvents : public EventTypes {
|
class IDataSocketEvents : public EventTypes {
|
||||||
public:
|
public:
|
||||||
IDataSocketEvents() :
|
IDataSocketEvents() :
|
||||||
|
|
|
@ -32,10 +32,6 @@ class EventQueueTimer;
|
||||||
// Event type registration classes.
|
// Event type registration classes.
|
||||||
class ClientEvents;
|
class ClientEvents;
|
||||||
class IStreamEvents;
|
class IStreamEvents;
|
||||||
class IpcClientEvents;
|
|
||||||
class IpcClientProxyEvents;
|
|
||||||
class IpcServerEvents;
|
|
||||||
class IpcServerProxyEvents;
|
|
||||||
class IDataSocketEvents;
|
class IDataSocketEvents;
|
||||||
class IListenSocketEvents;
|
class IListenSocketEvents;
|
||||||
class ISocketEvents;
|
class ISocketEvents;
|
||||||
|
@ -230,10 +226,6 @@ public:
|
||||||
|
|
||||||
virtual ClientEvents& forClient() = 0;
|
virtual ClientEvents& forClient() = 0;
|
||||||
virtual IStreamEvents& forIStream() = 0;
|
virtual IStreamEvents& forIStream() = 0;
|
||||||
virtual IpcClientEvents& forIpcClient() = 0;
|
|
||||||
virtual IpcClientProxyEvents& forIpcClientProxy() = 0;
|
|
||||||
virtual IpcServerEvents& forIpcServer() = 0;
|
|
||||||
virtual IpcServerProxyEvents& forIpcServerProxy() = 0;
|
|
||||||
virtual IDataSocketEvents& forIDataSocket() = 0;
|
virtual IDataSocketEvents& forIDataSocket() = 0;
|
||||||
virtual IListenSocketEvents& forIListenSocket() = 0;
|
virtual IListenSocketEvents& forIListenSocket() = 0;
|
||||||
virtual ISocketEvents& forISocket() = 0;
|
virtual ISocketEvents& forISocket() = 0;
|
||||||
|
|
|
@ -29,9 +29,6 @@
|
||||||
#include "core/ArgsBase.h"
|
#include "core/ArgsBase.h"
|
||||||
#include "core/XSynergy.h"
|
#include "core/XSynergy.h"
|
||||||
#include "core/protocol_types.h"
|
#include "core/protocol_types.h"
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "ipc/IpcServerProxy.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -64,7 +61,6 @@ App::App(IEventQueue* events, ArgsBase* args) :
|
||||||
m_args(args),
|
m_args(args),
|
||||||
m_fileLog(nullptr),
|
m_fileLog(nullptr),
|
||||||
m_appUtil(events),
|
m_appUtil(events),
|
||||||
m_ipcClient(nullptr),
|
|
||||||
m_socketMultiplexer(nullptr)
|
m_socketMultiplexer(nullptr)
|
||||||
{
|
{
|
||||||
assert(s_instance == nullptr);
|
assert(s_instance == nullptr);
|
||||||
|
@ -211,35 +207,6 @@ App::initApp(int argc, const char** argv)
|
||||||
loadConfig();
|
loadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
App::initIpcClient()
|
|
||||||
{
|
|
||||||
m_ipcClient = new IpcClient(m_events, m_socketMultiplexer);
|
|
||||||
m_ipcClient->connect();
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIpcClient().messageReceived(), m_ipcClient,
|
|
||||||
new TMethodEventJob<App>(this, &App::handleIpcMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
App::cleanupIpcClient()
|
|
||||||
{
|
|
||||||
m_ipcClient->disconnect();
|
|
||||||
m_events->removeHandler(m_events->forIpcClient().messageReceived(), m_ipcClient);
|
|
||||||
delete m_ipcClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
App::handleIpcMessage(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* m = dynamic_cast<IpcMessage*>(e.getDataObject());
|
|
||||||
if (m->type() == kIpcShutdown) {
|
|
||||||
LOG((CLOG_INFO "got ipc shutdown message"));
|
|
||||||
m_events->addEvent(Event(Event::kQuit));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
App::runEventsLoop(void* /*unused*/)
|
App::runEventsLoop(void* /*unused*/)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ipc/IpcClient.h"
|
|
||||||
#include "core/IApp.h"
|
#include "core/IApp.h"
|
||||||
#include "base/String.h"
|
#include "base/String.h"
|
||||||
#include "base/Log.h"
|
#include "base/Log.h"
|
||||||
|
@ -95,12 +94,7 @@ public:
|
||||||
|
|
||||||
void setEvents(EventQueue& events) { m_events = &events; }
|
void setEvents(EventQueue& events) { m_events = &events; }
|
||||||
|
|
||||||
private:
|
|
||||||
void handleIpcMessage(const Event&, void*);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initIpcClient();
|
|
||||||
void cleanupIpcClient();
|
|
||||||
void runEventsLoop(void*);
|
void runEventsLoop(void*);
|
||||||
|
|
||||||
bool m_suspended;
|
bool m_suspended;
|
||||||
|
@ -111,7 +105,6 @@ private:
|
||||||
static App* s_instance;
|
static App* s_instance;
|
||||||
FileLogOutputter* m_fileLog;
|
FileLogOutputter* m_fileLog;
|
||||||
ARCH_APP_UTIL m_appUtil;
|
ARCH_APP_UTIL m_appUtil;
|
||||||
IpcClient* m_ipcClient;
|
|
||||||
SocketMultiplexer* m_socketMultiplexer;
|
SocketMultiplexer* m_socketMultiplexer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -252,7 +252,7 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i)
|
||||||
argsBase().m_shouldExit = true;
|
argsBase().m_shouldExit = true;
|
||||||
}
|
}
|
||||||
else if (isArg(i, argc, argv, nullptr, "--ipc")) {
|
else if (isArg(i, argc, argv, nullptr, "--ipc")) {
|
||||||
argsBase().m_enableIpc = true;
|
LOG((CLOG_INFO "ignoring --ipc. The old IPC was removed."));
|
||||||
}
|
}
|
||||||
else if (isArg(i, argc, argv, nullptr, "--server")) {
|
else if (isArg(i, argc, argv, nullptr, "--server")) {
|
||||||
// supress error when --server is used
|
// supress error when --server is used
|
||||||
|
|
|
@ -38,7 +38,6 @@ m_pname(nullptr),
|
||||||
m_logFilter(nullptr),
|
m_logFilter(nullptr),
|
||||||
m_logFile(nullptr),
|
m_logFile(nullptr),
|
||||||
m_display(nullptr),
|
m_display(nullptr),
|
||||||
m_enableIpc(false),
|
|
||||||
m_enableDragDrop(false),
|
m_enableDragDrop(false),
|
||||||
m_shouldExit(false),
|
m_shouldExit(false),
|
||||||
m_profileDirectory(""),
|
m_profileDirectory(""),
|
||||||
|
|
|
@ -35,7 +35,6 @@ public:
|
||||||
const char* m_logFile;
|
const char* m_logFile;
|
||||||
const char* m_display;
|
const char* m_display;
|
||||||
String m_name;
|
String m_name;
|
||||||
bool m_enableIpc;
|
|
||||||
bool m_enableDragDrop;
|
bool m_enableDragDrop;
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
bool m_debugServiceWait;
|
bool m_debugServiceWait;
|
||||||
|
|
|
@ -36,5 +36,5 @@ endif()
|
||||||
add_library(core STATIC ${sources})
|
add_library(core STATIC ${sources})
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
target_link_libraries(core arch client ipc net base platform mt server)
|
target_link_libraries(core arch client net base platform mt server)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -453,12 +453,6 @@ ClientApp::mainLoop()
|
||||||
// start client, etc
|
// start client, etc
|
||||||
appUtil().startNode();
|
appUtil().startNode();
|
||||||
|
|
||||||
// init ipc client after node start, since create a new screen wipes out
|
|
||||||
// the event queue (the screen ctors call adoptBuffer).
|
|
||||||
if (argsBase().m_enableIpc) {
|
|
||||||
initIpcClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
// run event loop. if startClient() failed we're supposed to retry
|
// run event loop. if startClient() failed we're supposed to retry
|
||||||
// later. the timer installed by startClient() will take care of
|
// later. the timer installed by startClient() will take care of
|
||||||
// that.
|
// that.
|
||||||
|
@ -489,10 +483,6 @@ ClientApp::mainLoop()
|
||||||
updateStatus();
|
updateStatus();
|
||||||
LOG((CLOG_NOTE "stopped client"));
|
LOG((CLOG_NOTE "stopped client"));
|
||||||
|
|
||||||
if (argsBase().m_enableIpc) {
|
|
||||||
cleanupIpcClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
return kExitSuccess;
|
return kExitSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,6 @@
|
||||||
#include "core/ArgParser.h"
|
#include "core/ArgParser.h"
|
||||||
#include "core/ClientArgs.h"
|
#include "core/ClientArgs.h"
|
||||||
#include "core/ServerArgs.h"
|
#include "core/ServerArgs.h"
|
||||||
#include "ipc/IpcClientProxy.h"
|
|
||||||
#include "ipc/IpcLogOutputter.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "net/SocketMultiplexer.h"
|
#include "net/SocketMultiplexer.h"
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
|
@ -44,7 +41,6 @@
|
||||||
#include "core/Screen.h"
|
#include "core/Screen.h"
|
||||||
#include "platform/MSWindowsScreen.h"
|
#include "platform/MSWindowsScreen.h"
|
||||||
#include "platform/MSWindowsDebugOutputter.h"
|
#include "platform/MSWindowsDebugOutputter.h"
|
||||||
#include "platform/MSWindowsWatchdog.h"
|
|
||||||
#include "platform/MSWindowsEventQueueBuffer.h"
|
#include "platform/MSWindowsEventQueueBuffer.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
@ -82,8 +78,6 @@ winMainLoopStatic(int, const char**)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DaemonApp::DaemonApp() :
|
DaemonApp::DaemonApp() :
|
||||||
m_ipcServer(nullptr),
|
|
||||||
m_ipcLogOutputter(nullptr),
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
m_watchdog(nullptr),
|
m_watchdog(nullptr),
|
||||||
#endif
|
#endif
|
||||||
|
@ -206,53 +200,8 @@ DaemonApp::mainLoop(bool logToFile)
|
||||||
// create socket multiplexer. this must happen after daemonization
|
// create socket multiplexer. this must happen after daemonization
|
||||||
// on unix because threads evaporate across a fork().
|
// on unix because threads evaporate across a fork().
|
||||||
SocketMultiplexer multiplexer;
|
SocketMultiplexer multiplexer;
|
||||||
|
|
||||||
// uses event queue, must be created here.
|
|
||||||
m_ipcServer = new IpcServer(m_events, &multiplexer);
|
|
||||||
|
|
||||||
// send logging to gui via ipc, log system adopts outputter.
|
|
||||||
m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true);
|
|
||||||
CLOG->insert(m_ipcLogOutputter);
|
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
|
||||||
m_watchdog = new MSWindowsWatchdog(false, *m_ipcServer, *m_ipcLogOutputter);
|
|
||||||
m_watchdog->setFileLogOutputter(m_fileLogOutputter);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIpcServer().messageReceived(), m_ipcServer,
|
|
||||||
new TMethodEventJob<DaemonApp>(this, &DaemonApp::handleIpcMessage));
|
|
||||||
|
|
||||||
m_ipcServer->listen();
|
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
|
||||||
|
|
||||||
// install the platform event queue to handle service stop events.
|
|
||||||
m_events->adoptBuffer(new MSWindowsEventQueueBuffer(m_events));
|
|
||||||
|
|
||||||
String command = ARCH->setting("Command");
|
|
||||||
bool elevate = ARCH->setting("Elevate") == "1";
|
|
||||||
if (command != "") {
|
|
||||||
LOG((CLOG_INFO "using last known command: %s", command.c_str()));
|
|
||||||
m_watchdog->setCommand(command, elevate);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_watchdog->startAsync();
|
|
||||||
#endif
|
|
||||||
m_events->loop();
|
m_events->loop();
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
|
||||||
m_watchdog->stop();
|
|
||||||
delete m_watchdog;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_events->removeHandler(
|
|
||||||
m_events->forIpcServer().messageReceived(), m_ipcServer);
|
|
||||||
|
|
||||||
CLOG->remove(m_ipcLogOutputter);
|
|
||||||
delete m_ipcLogOutputter;
|
|
||||||
delete m_ipcServer;
|
|
||||||
|
|
||||||
DAEMON_RUNNING(false);
|
DAEMON_RUNNING(false);
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
|
@ -286,117 +235,3 @@ DaemonApp::logFilename()
|
||||||
|
|
||||||
return logFilename;
|
return logFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
DaemonApp::handleIpcMessage(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* m = dynamic_cast<IpcMessage*>(e.getDataObject());
|
|
||||||
switch (m->type()) {
|
|
||||||
case kIpcCommand: {
|
|
||||||
auto* cm = dynamic_cast<IpcCommandMessage*>(m);
|
|
||||||
String command = cm->command();
|
|
||||||
|
|
||||||
// if empty quotes, clear.
|
|
||||||
if (command == "\"\"") {
|
|
||||||
command.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!command.empty()) {
|
|
||||||
LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
|
|
||||||
|
|
||||||
std::vector<String> argsArray;
|
|
||||||
ArgParser::splitCommandString(command, argsArray);
|
|
||||||
ArgParser argParser(nullptr);
|
|
||||||
const char** argv = argParser.getArgv(argsArray);
|
|
||||||
ServerArgs serverArgs;
|
|
||||||
ClientArgs clientArgs;
|
|
||||||
auto argc = static_cast<int>(argsArray.size());
|
|
||||||
bool server = argsArray[0].find("synergys") != String::npos;
|
|
||||||
ArgsBase* argBase = nullptr;
|
|
||||||
|
|
||||||
if (server) {
|
|
||||||
argParser.parseServerArgs(serverArgs, argc, argv);
|
|
||||||
argBase = &serverArgs;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
argParser.parseClientArgs(clientArgs, argc, argv);
|
|
||||||
argBase = &clientArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] argv;
|
|
||||||
|
|
||||||
String logLevel(argBase->m_logFilter);
|
|
||||||
if (!logLevel.empty()) {
|
|
||||||
try {
|
|
||||||
// change log level based on that in the command string
|
|
||||||
// and change to that log level now.
|
|
||||||
ARCH->setting("LogLevel", logLevel);
|
|
||||||
CLOG->setFilter(logLevel.c_str());
|
|
||||||
}
|
|
||||||
catch (XArch& e) {
|
|
||||||
LOG((CLOG_ERR "failed to save LogLevel setting, %s", e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
|
||||||
String logFilename;
|
|
||||||
if (argBase->m_logFile != NULL) {
|
|
||||||
logFilename = String(argBase->m_logFile);
|
|
||||||
ARCH->setting("LogFilename", logFilename);
|
|
||||||
m_watchdog->setFileLogOutputter(m_fileLogOutputter);
|
|
||||||
command = ArgParser::assembleCommand(argsArray, "--log", 1);
|
|
||||||
LOG((CLOG_DEBUG "removed log file argument and filename %s from command ", logFilename.c_str()));
|
|
||||||
LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_watchdog->setFileLogOutputter(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_fileLogOutputter->setLogFilename(logFilename.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG((CLOG_DEBUG "empty command, elevate=%d", cm->elevate()));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// store command in system settings. this is used when the daemon
|
|
||||||
// next starts.
|
|
||||||
ARCH->setting("Command", command);
|
|
||||||
|
|
||||||
// TODO(andrew): it would be nice to store bools/ints...
|
|
||||||
ARCH->setting("Elevate", String(cm->elevate() ? "1" : "0"));
|
|
||||||
}
|
|
||||||
catch (XArch& e) {
|
|
||||||
LOG((CLOG_ERR "failed to save settings, %s", e.what()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
|
||||||
// tell the relauncher about the new command. this causes the
|
|
||||||
// relauncher to stop the existing command and start the new
|
|
||||||
// command.
|
|
||||||
m_watchdog->setCommand(command, cm->elevate());
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kIpcHello:
|
|
||||||
auto* hm = dynamic_cast<IpcHelloMessage*>(m);
|
|
||||||
String type;
|
|
||||||
switch (hm->clientType()) {
|
|
||||||
case kIpcClientGui: type = "gui"; break;
|
|
||||||
case kIpcClientNode: type = "node"; break;
|
|
||||||
default: type = "unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "ipc hello, type=%s", type.c_str()));
|
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
|
||||||
String watchdogStatus = m_watchdog->isProcessActive() ? "ok" : "error";
|
|
||||||
LOG((CLOG_INFO "watchdog status: %s", watchdogStatus.c_str()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_ipcLogOutputter->notifyBuffer();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,18 +19,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "arch/Arch.h"
|
#include "arch/Arch.h"
|
||||||
#include "ipc/IpcServer.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Event;
|
class Event;
|
||||||
class IpcLogOutputter;
|
|
||||||
class FileLogOutputter;
|
class FileLogOutputter;
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
class MSWindowsWatchdog;
|
class MSWindowsWatchdog;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class IEventQueue;
|
||||||
|
|
||||||
class DaemonApp {
|
class DaemonApp {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -43,7 +42,6 @@ private:
|
||||||
void daemonize();
|
void daemonize();
|
||||||
void foregroundError(const char* message);
|
void foregroundError(const char* message);
|
||||||
std::string logFilename();
|
std::string logFilename();
|
||||||
void handleIpcMessage(const Event&, void*);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static DaemonApp* s_instance;
|
static DaemonApp* s_instance;
|
||||||
|
@ -53,8 +51,6 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IpcServer* m_ipcServer;
|
|
||||||
IpcLogOutputter* m_ipcLogOutputter;
|
|
||||||
IEventQueue* m_events;
|
IEventQueue* m_events;
|
||||||
FileLogOutputter* m_fileLogOutputter;
|
FileLogOutputter* m_fileLogOutputter;
|
||||||
};
|
};
|
||||||
|
|
|
@ -704,12 +704,6 @@ ServerApp::mainLoop()
|
||||||
// start server, etc
|
// start server, etc
|
||||||
appUtil().startNode();
|
appUtil().startNode();
|
||||||
|
|
||||||
// init ipc client after node start, since create a new screen wipes out
|
|
||||||
// the event queue (the screen ctors call adoptBuffer).
|
|
||||||
if (argsBase().m_enableIpc) {
|
|
||||||
initIpcClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle hangup signal by reloading the server's configuration
|
// handle hangup signal by reloading the server's configuration
|
||||||
ARCH->setSignalHandler(Arch::kHANGUP, &reloadSignalHandler, nullptr);
|
ARCH->setSignalHandler(Arch::kHANGUP, &reloadSignalHandler, nullptr);
|
||||||
m_events->adoptHandler(m_events->forServerApp().reloadConfig(),
|
m_events->adoptHandler(m_events->forServerApp().reloadConfig(),
|
||||||
|
@ -762,10 +756,6 @@ ServerApp::mainLoop()
|
||||||
updateStatus();
|
updateStatus();
|
||||||
LOG((CLOG_NOTE "stopped server"));
|
LOG((CLOG_NOTE "stopped server"));
|
||||||
|
|
||||||
if (argsBase().m_enableIpc) {
|
|
||||||
cleanupIpcClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
return kExitSuccess;
|
return kExitSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# synergy -- mouse and keyboard sharing utility
|
|
||||||
# Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
# Copyright (C) 2009 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 LICENSE 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/>.
|
|
||||||
|
|
||||||
file(GLOB headers "*.h")
|
|
||||||
file(GLOB sources "*.cpp")
|
|
||||||
|
|
||||||
if (SYNERGY_ADD_HEADERS)
|
|
||||||
list(APPEND sources ${headers})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(ipc STATIC ${sources})
|
|
||||||
|
|
||||||
if (UNIX)
|
|
||||||
target_link_libraries(ipc arch base common mt io net core)
|
|
||||||
endif()
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/Ipc.h"
|
|
||||||
|
|
||||||
const char* kIpcMsgHello = "IHEL%1i";
|
|
||||||
const char* kIpcMsgLogLine = "ILOG%s";
|
|
||||||
const char* kIpcMsgCommand = "ICMD%s%1i";
|
|
||||||
const char* kIpcMsgShutdown = "ISDN";
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define IPC_HOST "127.0.0.1"
|
|
||||||
#define IPC_PORT 24801
|
|
||||||
|
|
||||||
enum EIpcMessage {
|
|
||||||
kIpcHello,
|
|
||||||
kIpcLogLine,
|
|
||||||
kIpcCommand,
|
|
||||||
kIpcShutdown,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum EIpcClientType {
|
|
||||||
kIpcClientUnknown,
|
|
||||||
kIpcClientGui,
|
|
||||||
kIpcClientNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
// handshake: node/gui -> daemon
|
|
||||||
// $1 = type, the client identifies it's self as gui or node (synergyc/s).
|
|
||||||
extern const char* kIpcMsgHello;
|
|
||||||
|
|
||||||
// log line: daemon -> gui
|
|
||||||
// $1 = aggregate log lines collected from synergys/c or the daemon itself.
|
|
||||||
extern const char* kIpcMsgLogLine;
|
|
||||||
|
|
||||||
// command: gui -> daemon
|
|
||||||
// $1 = command; the command for the daemon to launch, typically the full
|
|
||||||
// path to synergys/c. $2 = true when process must be elevated on ms windows.
|
|
||||||
extern const char* kIpcMsgCommand;
|
|
||||||
|
|
||||||
// shutdown: daemon -> node
|
|
||||||
// the daemon tells synergys/c to shut down gracefully.
|
|
||||||
extern const char* kIpcMsgShutdown;
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/IpcClient.h"
|
|
||||||
#include "base/TMethodEventJob.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "ipc/IpcServerProxy.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// IpcClient
|
|
||||||
//
|
|
||||||
|
|
||||||
IpcClient::IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
|
|
||||||
m_serverAddress(NetworkAddress(IPC_HOST, IPC_PORT)),
|
|
||||||
m_socket(events, socketMultiplexer),
|
|
||||||
m_server(nullptr),
|
|
||||||
m_events(events)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcClient::IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port) :
|
|
||||||
m_serverAddress(NetworkAddress(IPC_HOST, port)),
|
|
||||||
m_socket(events, socketMultiplexer),
|
|
||||||
m_server(nullptr),
|
|
||||||
m_events(events)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClient::init()
|
|
||||||
{
|
|
||||||
m_serverAddress.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcClient::~IpcClient()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClient::connect()
|
|
||||||
{
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIDataSocket().connected(), m_socket.getEventTarget(),
|
|
||||||
new TMethodEventJob<IpcClient>(
|
|
||||||
this, &IpcClient::handleConnected));
|
|
||||||
|
|
||||||
m_socket.connect(m_serverAddress);
|
|
||||||
m_server = new IpcServerProxy(m_socket, m_events);
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIpcServerProxy().messageReceived(), m_server,
|
|
||||||
new TMethodEventJob<IpcClient>(
|
|
||||||
this, &IpcClient::handleMessageReceived));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClient::disconnect()
|
|
||||||
{
|
|
||||||
m_events->removeHandler(m_events->forIDataSocket().connected(), m_socket.getEventTarget());
|
|
||||||
m_events->removeHandler(m_events->forIpcServerProxy().messageReceived(), m_server);
|
|
||||||
|
|
||||||
m_server->disconnect();
|
|
||||||
delete m_server;
|
|
||||||
m_server = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClient::send(const IpcMessage& message)
|
|
||||||
{
|
|
||||||
assert(m_server != nullptr);
|
|
||||||
m_server->send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClient::handleConnected(const Event& /*unused*/, void* /*unused*/)
|
|
||||||
{
|
|
||||||
m_events->addEvent(Event(
|
|
||||||
m_events->forIpcClient().connected(), this, m_server, Event::kDontFreeData));
|
|
||||||
|
|
||||||
IpcHelloMessage message(kIpcClientNode);
|
|
||||||
send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClient::handleMessageReceived(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
Event event(m_events->forIpcClient().messageReceived(), this);
|
|
||||||
event.setDataObject(e.getDataObject());
|
|
||||||
m_events->addEvent(event);
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "net/NetworkAddress.h"
|
|
||||||
#include "net/TCPSocket.h"
|
|
||||||
#include "base/EventTypes.h"
|
|
||||||
|
|
||||||
class IpcServerProxy;
|
|
||||||
class IpcMessage;
|
|
||||||
class IEventQueue;
|
|
||||||
class SocketMultiplexer;
|
|
||||||
|
|
||||||
//! IPC client for communication between daemon and GUI.
|
|
||||||
/*!
|
|
||||||
* See \ref IpcServer description.
|
|
||||||
*/
|
|
||||||
class IpcClient {
|
|
||||||
public:
|
|
||||||
IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
|
|
||||||
IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port);
|
|
||||||
virtual ~IpcClient();
|
|
||||||
|
|
||||||
//! @name manipulators
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Connects to the IPC server at localhost.
|
|
||||||
void connect();
|
|
||||||
|
|
||||||
//! Disconnects from the IPC server.
|
|
||||||
void disconnect();
|
|
||||||
|
|
||||||
//! Sends a message to the server.
|
|
||||||
void send(const IpcMessage& message);
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
void handleConnected(const Event&, void*);
|
|
||||||
void handleMessageReceived(const Event&, void*);
|
|
||||||
|
|
||||||
private:
|
|
||||||
NetworkAddress m_serverAddress;
|
|
||||||
TCPSocket m_socket;
|
|
||||||
IpcServerProxy* m_server;
|
|
||||||
IEventQueue* m_events;
|
|
||||||
};
|
|
|
@ -1,194 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/IpcClientProxy.h"
|
|
||||||
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
#include "base/Log.h"
|
|
||||||
#include "base/TMethodEventJob.h"
|
|
||||||
#include "core/ProtocolUtil.h"
|
|
||||||
#include "io/IStream.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// IpcClientProxy
|
|
||||||
//
|
|
||||||
|
|
||||||
IpcClientProxy::IpcClientProxy(synergy::IStream& stream, IEventQueue* events) :
|
|
||||||
m_stream(stream),
|
|
||||||
m_clientType(kIpcClientUnknown),
|
|
||||||
m_disconnecting(false),
|
|
||||||
m_readMutex(ARCH->newMutex()),
|
|
||||||
m_writeMutex(ARCH->newMutex()),
|
|
||||||
m_events(events)
|
|
||||||
{
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIStream().inputReady(), stream.getEventTarget(),
|
|
||||||
new TMethodEventJob<IpcClientProxy>(
|
|
||||||
this, &IpcClientProxy::handleData));
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIStream().outputError(), stream.getEventTarget(),
|
|
||||||
new TMethodEventJob<IpcClientProxy>(
|
|
||||||
this, &IpcClientProxy::handleWriteError));
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIStream().inputShutdown(), stream.getEventTarget(),
|
|
||||||
new TMethodEventJob<IpcClientProxy>(
|
|
||||||
this, &IpcClientProxy::handleDisconnect));
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIStream().outputShutdown(), stream.getEventTarget(),
|
|
||||||
new TMethodEventJob<IpcClientProxy>(
|
|
||||||
this, &IpcClientProxy::handleWriteError));
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcClientProxy::~IpcClientProxy()
|
|
||||||
{
|
|
||||||
m_events->removeHandler(
|
|
||||||
m_events->forIStream().inputReady(), m_stream.getEventTarget());
|
|
||||||
m_events->removeHandler(
|
|
||||||
m_events->forIStream().outputError(), m_stream.getEventTarget());
|
|
||||||
m_events->removeHandler(
|
|
||||||
m_events->forIStream().inputShutdown(), m_stream.getEventTarget());
|
|
||||||
m_events->removeHandler(
|
|
||||||
m_events->forIStream().outputShutdown(), m_stream.getEventTarget());
|
|
||||||
|
|
||||||
// don't delete the stream while it's being used.
|
|
||||||
ARCH->lockMutex(m_readMutex);
|
|
||||||
ARCH->lockMutex(m_writeMutex);
|
|
||||||
delete &m_stream;
|
|
||||||
ARCH->unlockMutex(m_readMutex);
|
|
||||||
ARCH->unlockMutex(m_writeMutex);
|
|
||||||
|
|
||||||
ARCH->closeMutex(m_readMutex);
|
|
||||||
ARCH->closeMutex(m_writeMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClientProxy::handleDisconnect(const Event& /*unused*/, void* /*unused*/)
|
|
||||||
{
|
|
||||||
disconnect();
|
|
||||||
LOG((CLOG_DEBUG "ipc client disconnected"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClientProxy::handleWriteError(const Event& /*unused*/, void* /*unused*/)
|
|
||||||
{
|
|
||||||
disconnect();
|
|
||||||
LOG((CLOG_DEBUG "ipc client write error"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClientProxy::handleData(const Event& /*unused*/, void* /*unused*/)
|
|
||||||
{
|
|
||||||
// don't allow the dtor to destroy the stream while we're using it.
|
|
||||||
ArchMutexLock lock(m_readMutex);
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "start ipc handle data"));
|
|
||||||
|
|
||||||
UInt8 code[4];
|
|
||||||
UInt32 n = m_stream.read(code, 4);
|
|
||||||
while (n != 0) {
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "ipc read: %c%c%c%c",
|
|
||||||
code[0], code[1], code[2], code[3]));
|
|
||||||
|
|
||||||
IpcMessage* m = nullptr;
|
|
||||||
if (memcmp(code, kIpcMsgHello, 4) == 0) {
|
|
||||||
m = parseHello();
|
|
||||||
}
|
|
||||||
else if (memcmp(code, kIpcMsgCommand, 4) == 0) {
|
|
||||||
m = parseCommand();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG((CLOG_ERR "invalid ipc message"));
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't delete with this event; the data is passed to a new event.
|
|
||||||
Event e(m_events->forIpcClientProxy().messageReceived(), this, nullptr, Event::kDontFreeData);
|
|
||||||
e.setDataObject(m);
|
|
||||||
m_events->addEvent(e);
|
|
||||||
|
|
||||||
n = m_stream.read(code, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "finished ipc handle data"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClientProxy::send(const IpcMessage& message)
|
|
||||||
{
|
|
||||||
// don't allow other threads to write until we've finished the entire
|
|
||||||
// message. stream write is locked, but only for that single write.
|
|
||||||
// also, don't allow the dtor to destroy the stream while we're using it.
|
|
||||||
ArchMutexLock lock(m_writeMutex);
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG4 "ipc write: %d", message.type()));
|
|
||||||
|
|
||||||
switch (message.type()) {
|
|
||||||
case kIpcLogLine: {
|
|
||||||
const auto& llm = dynamic_cast<const IpcLogLineMessage&>(message);
|
|
||||||
const String logLine = llm.logLine();
|
|
||||||
ProtocolUtil::writef(&m_stream, kIpcMsgLogLine, &logLine);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kIpcShutdown:
|
|
||||||
ProtocolUtil::writef(&m_stream, kIpcMsgShutdown);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG((CLOG_ERR "ipc message not supported: %d", message.type()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHelloMessage*
|
|
||||||
IpcClientProxy::parseHello()
|
|
||||||
{
|
|
||||||
UInt8 type;
|
|
||||||
ProtocolUtil::readf(&m_stream, kIpcMsgHello + 4, &type);
|
|
||||||
|
|
||||||
m_clientType = static_cast<EIpcClientType>(type);
|
|
||||||
|
|
||||||
// must be deleted by event handler.
|
|
||||||
return new IpcHelloMessage(m_clientType);
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcCommandMessage*
|
|
||||||
IpcClientProxy::parseCommand()
|
|
||||||
{
|
|
||||||
String command;
|
|
||||||
UInt8 elevate;
|
|
||||||
ProtocolUtil::readf(&m_stream, kIpcMsgCommand + 4, &command, &elevate);
|
|
||||||
|
|
||||||
// must be deleted by event handler.
|
|
||||||
return new IpcCommandMessage(command, elevate != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcClientProxy::disconnect()
|
|
||||||
{
|
|
||||||
LOG((CLOG_DEBUG "ipc disconnect, closing stream"));
|
|
||||||
m_disconnecting = true;
|
|
||||||
m_stream.close();
|
|
||||||
m_events->addEvent(Event(m_events->forIpcClientProxy().disconnected(), this));
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "arch/IArchMultithread.h"
|
|
||||||
#include "base/EventTypes.h"
|
|
||||||
#include "base/Event.h"
|
|
||||||
|
|
||||||
namespace synergy { class IStream; }
|
|
||||||
class IpcMessage;
|
|
||||||
class IpcCommandMessage;
|
|
||||||
class IpcHelloMessage;
|
|
||||||
class IEventQueue;
|
|
||||||
|
|
||||||
class IpcClientProxy {
|
|
||||||
friend class IpcServer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
IpcClientProxy(synergy::IStream& stream, IEventQueue* events);
|
|
||||||
virtual ~IpcClientProxy();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void send(const IpcMessage& message);
|
|
||||||
void handleData(const Event&, void*);
|
|
||||||
void handleDisconnect(const Event&, void*);
|
|
||||||
void handleWriteError(const Event&, void*);
|
|
||||||
IpcHelloMessage* parseHello();
|
|
||||||
IpcCommandMessage* parseCommand();
|
|
||||||
void disconnect();
|
|
||||||
|
|
||||||
private:
|
|
||||||
synergy::IStream& m_stream;
|
|
||||||
EIpcClientType m_clientType;
|
|
||||||
bool m_disconnecting;
|
|
||||||
ArchMutex m_readMutex;
|
|
||||||
ArchMutex m_writeMutex;
|
|
||||||
IEventQueue* m_events;
|
|
||||||
};
|
|
|
@ -1,228 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/IpcLogOutputter.h"
|
|
||||||
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
#include "arch/XArch.h"
|
|
||||||
#include "base/Event.h"
|
|
||||||
#include "base/EventQueue.h"
|
|
||||||
#include "base/TMethodEventJob.h"
|
|
||||||
#include "base/TMethodJob.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcClientProxy.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "ipc/IpcServer.h"
|
|
||||||
#include "mt/Thread.h"
|
|
||||||
|
|
||||||
enum EIpcLogOutputter {
|
|
||||||
kBufferMaxSize = 1000,
|
|
||||||
kMaxSendLines = 100,
|
|
||||||
kBufferRateWriteLimit = 1000, // writes per kBufferRateTime
|
|
||||||
kBufferRateTimeLimit = 1 // seconds
|
|
||||||
};
|
|
||||||
|
|
||||||
IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread) :
|
|
||||||
m_ipcServer(ipcServer),
|
|
||||||
m_bufferMutex(ARCH->newMutex()),
|
|
||||||
m_sending(false),
|
|
||||||
m_bufferThread(nullptr),
|
|
||||||
m_running(false),
|
|
||||||
m_notifyCond(ARCH->newCondVar()),
|
|
||||||
m_notifyMutex(ARCH->newMutex()),
|
|
||||||
m_bufferThreadId(0),
|
|
||||||
m_bufferWaiting(false),
|
|
||||||
m_bufferMaxSize(kBufferMaxSize),
|
|
||||||
m_bufferRateWriteLimit(kBufferRateWriteLimit),
|
|
||||||
m_bufferRateTimeLimit(kBufferRateTimeLimit),
|
|
||||||
m_bufferWriteCount(0),
|
|
||||||
m_bufferRateStart(ARCH->time()),
|
|
||||||
m_clientType(clientType),
|
|
||||||
m_runningMutex(ARCH->newMutex())
|
|
||||||
{
|
|
||||||
if (useThread) {
|
|
||||||
m_bufferThread = new Thread(new TMethodJob<IpcLogOutputter>(
|
|
||||||
this, &IpcLogOutputter::bufferThread));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcLogOutputter::~IpcLogOutputter()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
|
|
||||||
ARCH->closeMutex(m_bufferMutex);
|
|
||||||
|
|
||||||
if (m_bufferThread != nullptr) {
|
|
||||||
m_bufferThread->cancel();
|
|
||||||
m_bufferThread->wait();
|
|
||||||
delete m_bufferThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARCH->closeCondVar(m_notifyCond);
|
|
||||||
ARCH->closeMutex(m_notifyMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::open(const char* /*title*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::close()
|
|
||||||
{
|
|
||||||
if (m_bufferThread != nullptr) {
|
|
||||||
ArchMutexLock lock(m_runningMutex);
|
|
||||||
m_running = false;
|
|
||||||
notifyBuffer();
|
|
||||||
m_bufferThread->wait(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::show(bool /*showIfEmpty*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IpcLogOutputter::write(ELevel /*level*/, const char* text)
|
|
||||||
{
|
|
||||||
// ignore events from the buffer thread (would cause recursion).
|
|
||||||
if (m_bufferThread != nullptr &&
|
|
||||||
Thread::getCurrentThread().getID() == m_bufferThreadId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendBuffer(text);
|
|
||||||
notifyBuffer();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::appendBuffer(const String& text)
|
|
||||||
{
|
|
||||||
ArchMutexLock lock(m_bufferMutex);
|
|
||||||
|
|
||||||
double elapsed = ARCH->time() - m_bufferRateStart;
|
|
||||||
if (elapsed < m_bufferRateTimeLimit) {
|
|
||||||
if (m_bufferWriteCount >= m_bufferRateWriteLimit) {
|
|
||||||
// discard the log line if we've logged too much.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_bufferWriteCount = 0;
|
|
||||||
m_bufferRateStart = ARCH->time();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_buffer.size() >= m_bufferMaxSize) {
|
|
||||||
// if the queue is exceeds size limit,
|
|
||||||
// throw away the oldest item
|
|
||||||
m_buffer.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_buffer.push_back(text);
|
|
||||||
m_bufferWriteCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IpcLogOutputter::isRunning()
|
|
||||||
{
|
|
||||||
ArchMutexLock lock(m_runningMutex);
|
|
||||||
return m_running;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::bufferThread(void* /*unused*/)
|
|
||||||
{
|
|
||||||
m_bufferThreadId = m_bufferThread->getID();
|
|
||||||
m_running = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (isRunning()) {
|
|
||||||
if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) {
|
|
||||||
ArchMutexLock lock(m_notifyMutex);
|
|
||||||
ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (XArch& e) {
|
|
||||||
LOG((CLOG_ERR "ipc log buffer thread error, %s", e.what()));
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "ipc log buffer thread finished"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::notifyBuffer()
|
|
||||||
{
|
|
||||||
ArchMutexLock lock(m_notifyMutex);
|
|
||||||
ARCH->broadcastCondVar(m_notifyCond);
|
|
||||||
}
|
|
||||||
|
|
||||||
String
|
|
||||||
IpcLogOutputter::getChunk(size_t count)
|
|
||||||
{
|
|
||||||
ArchMutexLock lock(m_bufferMutex);
|
|
||||||
|
|
||||||
if (m_buffer.size() < count) {
|
|
||||||
count = m_buffer.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
String chunk;
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
chunk.append(m_buffer.front());
|
|
||||||
chunk.append("\n");
|
|
||||||
m_buffer.pop_front();
|
|
||||||
}
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::sendBuffer()
|
|
||||||
{
|
|
||||||
if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcLogLineMessage message(getChunk(kMaxSendLines));
|
|
||||||
m_sending = true;
|
|
||||||
m_ipcServer.send(message, kIpcClientGui);
|
|
||||||
m_sending = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::bufferMaxSize(UInt16 bufferMaxSize)
|
|
||||||
{
|
|
||||||
m_bufferMaxSize = bufferMaxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt16
|
|
||||||
IpcLogOutputter::bufferMaxSize() const
|
|
||||||
{
|
|
||||||
return m_bufferMaxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcLogOutputter::bufferRateLimit(UInt16 writeLimit, double timeLimit)
|
|
||||||
{
|
|
||||||
m_bufferRateWriteLimit = writeLimit;
|
|
||||||
m_bufferRateTimeLimit = timeLimit;
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
#include "arch/IArchMultithread.h"
|
|
||||||
#include "base/ILogOutputter.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
|
|
||||||
class IpcServer;
|
|
||||||
class Event;
|
|
||||||
class IpcClientProxy;
|
|
||||||
|
|
||||||
//! Write log to GUI over IPC
|
|
||||||
/*!
|
|
||||||
This outputter writes output to the GUI via IPC.
|
|
||||||
*/
|
|
||||||
class IpcLogOutputter : public ILogOutputter {
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
If \p useThread is \c true, the buffer will be sent using a thread.
|
|
||||||
If \p useThread is \c false, then the buffer needs to be sent manually
|
|
||||||
using the \c sendBuffer() function.
|
|
||||||
*/
|
|
||||||
IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread);
|
|
||||||
virtual ~IpcLogOutputter();
|
|
||||||
|
|
||||||
// ILogOutputter overrides
|
|
||||||
virtual void open(const char* title);
|
|
||||||
virtual void close();
|
|
||||||
virtual void show(bool showIfEmpty);
|
|
||||||
virtual bool write(ELevel level, const char* text);
|
|
||||||
|
|
||||||
//! @name manipulators
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Notify that the buffer should be sent.
|
|
||||||
void notifyBuffer();
|
|
||||||
|
|
||||||
//! Set the buffer size
|
|
||||||
/*!
|
|
||||||
Set the maximum size of the buffer to protect memory
|
|
||||||
from runaway logging.
|
|
||||||
*/
|
|
||||||
void bufferMaxSize(UInt16 bufferMaxSize);
|
|
||||||
|
|
||||||
//! Set the rate limit
|
|
||||||
/*!
|
|
||||||
Set the maximum number of \p writeRate for every \p timeRate in seconds.
|
|
||||||
*/
|
|
||||||
void bufferRateLimit(UInt16 writeLimit, double timeLimit);
|
|
||||||
|
|
||||||
//! Send the buffer
|
|
||||||
/*!
|
|
||||||
Sends a chunk of the buffer to the IPC server, normally called
|
|
||||||
when threaded mode is on.
|
|
||||||
*/
|
|
||||||
void sendBuffer();
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
//! @name accessors
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Get the buffer size
|
|
||||||
/*!
|
|
||||||
Returns the maximum size of the buffer.
|
|
||||||
*/
|
|
||||||
UInt16 bufferMaxSize() const;
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
void bufferThread(void*);
|
|
||||||
String getChunk(size_t count);
|
|
||||||
void appendBuffer(const String& text);
|
|
||||||
bool isRunning();
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::deque<String> Buffer;
|
|
||||||
|
|
||||||
IpcServer& m_ipcServer;
|
|
||||||
Buffer m_buffer;
|
|
||||||
ArchMutex m_bufferMutex;
|
|
||||||
bool m_sending;
|
|
||||||
Thread* m_bufferThread;
|
|
||||||
bool m_running;
|
|
||||||
ArchCond m_notifyCond;
|
|
||||||
ArchMutex m_notifyMutex;
|
|
||||||
bool m_bufferWaiting;
|
|
||||||
IArchMultithread::ThreadID
|
|
||||||
m_bufferThreadId;
|
|
||||||
UInt16 m_bufferMaxSize;
|
|
||||||
UInt16 m_bufferRateWriteLimit;
|
|
||||||
double m_bufferRateTimeLimit;
|
|
||||||
UInt16 m_bufferWriteCount;
|
|
||||||
double m_bufferRateStart;
|
|
||||||
EIpcClientType m_clientType;
|
|
||||||
ArchMutex m_runningMutex;
|
|
||||||
};
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/IpcMessage.h"
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
|
|
||||||
IpcMessage::IpcMessage(UInt8 type) :
|
|
||||||
m_type(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcMessage::~IpcMessage()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
IpcHelloMessage::IpcHelloMessage(EIpcClientType clientType) :
|
|
||||||
IpcMessage(kIpcHello),
|
|
||||||
m_clientType(clientType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHelloMessage::~IpcHelloMessage()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
IpcShutdownMessage::IpcShutdownMessage() :
|
|
||||||
IpcMessage(kIpcShutdown)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcShutdownMessage::~IpcShutdownMessage()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
IpcLogLineMessage::IpcLogLineMessage(String logLine) :
|
|
||||||
IpcMessage(kIpcLogLine),
|
|
||||||
m_logLine(std::move(logLine))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcLogLineMessage::~IpcLogLineMessage()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
IpcCommandMessage::IpcCommandMessage(String command, bool elevate) :
|
|
||||||
IpcMessage(kIpcCommand),
|
|
||||||
m_command(std::move(command)),
|
|
||||||
m_elevate(elevate)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcCommandMessage::~IpcCommandMessage()
|
|
||||||
= default;
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "base/EventTypes.h"
|
|
||||||
#include "base/String.h"
|
|
||||||
#include "base/Event.h"
|
|
||||||
|
|
||||||
class IpcMessage : public EventData {
|
|
||||||
public:
|
|
||||||
virtual ~IpcMessage();
|
|
||||||
|
|
||||||
//! Gets the message type ID.
|
|
||||||
UInt8 type() const { return m_type; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
IpcMessage(UInt8 type);
|
|
||||||
|
|
||||||
private:
|
|
||||||
UInt8 m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IpcHelloMessage : public IpcMessage {
|
|
||||||
public:
|
|
||||||
IpcHelloMessage(EIpcClientType clientType);
|
|
||||||
virtual ~IpcHelloMessage();
|
|
||||||
|
|
||||||
//! Gets the message type ID.
|
|
||||||
EIpcClientType clientType() const { return m_clientType; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
EIpcClientType m_clientType;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IpcShutdownMessage : public IpcMessage {
|
|
||||||
public:
|
|
||||||
IpcShutdownMessage();
|
|
||||||
virtual ~IpcShutdownMessage();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IpcLogLineMessage : public IpcMessage {
|
|
||||||
public:
|
|
||||||
IpcLogLineMessage(String logLine);
|
|
||||||
virtual ~IpcLogLineMessage();
|
|
||||||
|
|
||||||
//! Gets the log line.
|
|
||||||
String logLine() const { return m_logLine; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
String m_logLine;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IpcCommandMessage : public IpcMessage {
|
|
||||||
public:
|
|
||||||
IpcCommandMessage(String command, bool elevate);
|
|
||||||
virtual ~IpcCommandMessage();
|
|
||||||
|
|
||||||
//! Gets the command.
|
|
||||||
String command() const { return m_command; }
|
|
||||||
|
|
||||||
//! Gets whether or not the process should be elevated on MS Windows.
|
|
||||||
bool elevate() const { return m_elevate; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
String m_command;
|
|
||||||
bool m_elevate;
|
|
||||||
};
|
|
|
@ -1,187 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/IpcServer.h"
|
|
||||||
|
|
||||||
#include "base/Event.h"
|
|
||||||
#include "base/IEventQueue.h"
|
|
||||||
#include "base/Log.h"
|
|
||||||
#include "base/TMethodEventJob.h"
|
|
||||||
#include "io/IStream.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcClientProxy.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "net/IDataSocket.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// IpcServer
|
|
||||||
//
|
|
||||||
|
|
||||||
IpcServer::IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
|
|
||||||
m_mock(false),
|
|
||||||
m_events(events),
|
|
||||||
m_socketMultiplexer(socketMultiplexer),
|
|
||||||
m_socket(nullptr),
|
|
||||||
m_address(NetworkAddress(IPC_HOST, IPC_PORT))
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcServer::IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port) :
|
|
||||||
m_mock(false),
|
|
||||||
m_events(events),
|
|
||||||
m_socketMultiplexer(socketMultiplexer),
|
|
||||||
m_address(NetworkAddress(IPC_HOST, port))
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::init()
|
|
||||||
{
|
|
||||||
m_socket = new TCPListenSocket(m_events, m_socketMultiplexer);
|
|
||||||
|
|
||||||
m_clientsMutex = ARCH->newMutex();
|
|
||||||
m_address.resolve();
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIListenSocket().connecting(), m_socket,
|
|
||||||
new TMethodEventJob<IpcServer>(
|
|
||||||
this, &IpcServer::handleClientConnecting));
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcServer::~IpcServer()
|
|
||||||
{
|
|
||||||
if (m_mock) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
delete m_socket;
|
|
||||||
|
|
||||||
|
|
||||||
ARCH->lockMutex(m_clientsMutex);
|
|
||||||
ClientList::iterator it;
|
|
||||||
for (it = m_clients.begin(); it != m_clients.end(); it++) {
|
|
||||||
deleteClient(*it);
|
|
||||||
}
|
|
||||||
m_clients.clear();
|
|
||||||
ARCH->unlockMutex(m_clientsMutex);
|
|
||||||
ARCH->closeMutex(m_clientsMutex);
|
|
||||||
|
|
||||||
m_events->removeHandler(m_events->forIListenSocket().connecting(), m_socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::listen()
|
|
||||||
{
|
|
||||||
m_socket->bind(m_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::handleClientConnecting(const Event& /*unused*/, void* /*unused*/)
|
|
||||||
{
|
|
||||||
synergy::IStream* stream = m_socket->accept();
|
|
||||||
if (stream == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "accepted ipc client connection"));
|
|
||||||
|
|
||||||
ARCH->lockMutex(m_clientsMutex);
|
|
||||||
auto* proxy = new IpcClientProxy(*stream, m_events);
|
|
||||||
m_clients.push_back(proxy);
|
|
||||||
ARCH->unlockMutex(m_clientsMutex);
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIpcClientProxy().disconnected(), proxy,
|
|
||||||
new TMethodEventJob<IpcServer>(
|
|
||||||
this, &IpcServer::handleClientDisconnected));
|
|
||||||
|
|
||||||
m_events->adoptHandler(
|
|
||||||
m_events->forIpcClientProxy().messageReceived(), proxy,
|
|
||||||
new TMethodEventJob<IpcServer>(
|
|
||||||
this, &IpcServer::handleMessageReceived));
|
|
||||||
|
|
||||||
m_events->addEvent(Event(
|
|
||||||
m_events->forIpcServer().clientConnected(), this, proxy, Event::kDontFreeData));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::handleClientDisconnected(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* proxy = static_cast<IpcClientProxy*>(e.getTarget());
|
|
||||||
|
|
||||||
ArchMutexLock lock(m_clientsMutex);
|
|
||||||
m_clients.remove(proxy);
|
|
||||||
deleteClient(proxy);
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "ipc client proxy removed, connected=%d", m_clients.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::handleMessageReceived(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
Event event(m_events->forIpcServer().messageReceived(), this);
|
|
||||||
event.setDataObject(e.getDataObject());
|
|
||||||
m_events->addEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::deleteClient(IpcClientProxy* proxy)
|
|
||||||
{
|
|
||||||
m_events->removeHandler(m_events->forIpcClientProxy().messageReceived(), proxy);
|
|
||||||
m_events->removeHandler(m_events->forIpcClientProxy().disconnected(), proxy);
|
|
||||||
delete proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
IpcServer::hasClients(EIpcClientType clientType) const
|
|
||||||
{
|
|
||||||
ArchMutexLock lock(m_clientsMutex);
|
|
||||||
|
|
||||||
if (m_clients.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientList::const_iterator it;
|
|
||||||
for (it = m_clients.begin(); it != m_clients.end(); it++) {
|
|
||||||
// at least one client is alive and type matches, there are clients.
|
|
||||||
IpcClientProxy* p = *it;
|
|
||||||
if (!p->m_disconnecting && p->m_clientType == clientType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// all clients must be disconnecting, no active clients.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServer::send(const IpcMessage& message, EIpcClientType filterType)
|
|
||||||
{
|
|
||||||
ArchMutexLock lock(m_clientsMutex);
|
|
||||||
|
|
||||||
ClientList::iterator it;
|
|
||||||
for (it = m_clients.begin(); it != m_clients.end(); it++) {
|
|
||||||
IpcClientProxy* proxy = *it;
|
|
||||||
if (proxy->m_clientType == filterType) {
|
|
||||||
proxy->send(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "net/TCPListenSocket.h"
|
|
||||||
#include "net/NetworkAddress.h"
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
#include "base/EventTypes.h"
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
class Event;
|
|
||||||
class IpcClientProxy;
|
|
||||||
class IpcMessage;
|
|
||||||
class IEventQueue;
|
|
||||||
class SocketMultiplexer;
|
|
||||||
|
|
||||||
//! IPC server for communication between daemon and GUI.
|
|
||||||
/*!
|
|
||||||
The IPC server listens on localhost. The IPC client runs on both the
|
|
||||||
client/server process or the GUI. The IPC server runs on the daemon process.
|
|
||||||
This allows the GUI to send config changes to the daemon and client/server,
|
|
||||||
and allows the daemon and client/server to send log data to the GUI.
|
|
||||||
*/
|
|
||||||
class IpcServer {
|
|
||||||
public:
|
|
||||||
IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
|
|
||||||
IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port);
|
|
||||||
virtual ~IpcServer();
|
|
||||||
|
|
||||||
//! @name manipulators
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Opens a TCP socket only allowing local connections.
|
|
||||||
virtual void listen();
|
|
||||||
|
|
||||||
//! Send a message to all clients matching the filter type.
|
|
||||||
virtual void send(const IpcMessage& message, EIpcClientType filterType);
|
|
||||||
|
|
||||||
//@}
|
|
||||||
//! @name accessors
|
|
||||||
//@{
|
|
||||||
|
|
||||||
//! Returns true when there are clients of the specified type connected.
|
|
||||||
virtual bool hasClients(EIpcClientType clientType) const;
|
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
void handleClientConnecting(const Event&, void*);
|
|
||||||
void handleClientDisconnected(const Event&, void*);
|
|
||||||
void handleMessageReceived(const Event&, void*);
|
|
||||||
void deleteClient(IpcClientProxy* proxy);
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::list<IpcClientProxy*> ClientList;
|
|
||||||
|
|
||||||
bool m_mock;
|
|
||||||
IEventQueue* m_events;
|
|
||||||
SocketMultiplexer* m_socketMultiplexer;
|
|
||||||
TCPListenSocket* m_socket{};
|
|
||||||
NetworkAddress m_address;
|
|
||||||
ClientList m_clients;
|
|
||||||
ArchMutex m_clientsMutex{};
|
|
||||||
|
|
||||||
#ifdef TEST_ENV
|
|
||||||
public:
|
|
||||||
IpcServer() :
|
|
||||||
m_mock(true),
|
|
||||||
m_events(nullptr),
|
|
||||||
m_socketMultiplexer(nullptr),
|
|
||||||
m_socket(nullptr) { }
|
|
||||||
#endif
|
|
||||||
};
|
|
|
@ -1,123 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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 "ipc/IpcServerProxy.h"
|
|
||||||
|
|
||||||
#include "base/Log.h"
|
|
||||||
#include "base/TMethodEventJob.h"
|
|
||||||
#include "core/ProtocolUtil.h"
|
|
||||||
#include "io/IStream.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// IpcServerProxy
|
|
||||||
//
|
|
||||||
|
|
||||||
IpcServerProxy::IpcServerProxy(synergy::IStream& stream, IEventQueue* events) :
|
|
||||||
m_stream(stream),
|
|
||||||
m_events(events)
|
|
||||||
{
|
|
||||||
m_events->adoptHandler(m_events->forIStream().inputReady(),
|
|
||||||
stream.getEventTarget(),
|
|
||||||
new TMethodEventJob<IpcServerProxy>(
|
|
||||||
this, &IpcServerProxy::handleData));
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcServerProxy::~IpcServerProxy()
|
|
||||||
{
|
|
||||||
m_events->removeHandler(m_events->forIStream().inputReady(),
|
|
||||||
m_stream.getEventTarget());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServerProxy::handleData(const Event& /*unused*/, void* /*unused*/)
|
|
||||||
{
|
|
||||||
LOG((CLOG_DEBUG "start ipc handle data"));
|
|
||||||
|
|
||||||
UInt8 code[4];
|
|
||||||
UInt32 n = m_stream.read(code, 4);
|
|
||||||
while (n != 0) {
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "ipc read: %c%c%c%c",
|
|
||||||
code[0], code[1], code[2], code[3]));
|
|
||||||
|
|
||||||
IpcMessage* m = nullptr;
|
|
||||||
if (memcmp(code, kIpcMsgLogLine, 4) == 0) {
|
|
||||||
m = parseLogLine();
|
|
||||||
}
|
|
||||||
else if (memcmp(code, kIpcMsgShutdown, 4) == 0) {
|
|
||||||
m = new IpcShutdownMessage();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG((CLOG_ERR "invalid ipc message"));
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't delete with this event; the data is passed to a new event.
|
|
||||||
Event e(m_events->forIpcServerProxy().messageReceived(), this, nullptr, Event::kDontFreeData);
|
|
||||||
e.setDataObject(m);
|
|
||||||
m_events->addEvent(e);
|
|
||||||
|
|
||||||
n = m_stream.read(code, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "finished ipc handle data"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServerProxy::send(const IpcMessage& message)
|
|
||||||
{
|
|
||||||
LOG((CLOG_DEBUG4 "ipc write: %d", message.type()));
|
|
||||||
|
|
||||||
switch (message.type()) {
|
|
||||||
case kIpcHello: {
|
|
||||||
const auto& hm = dynamic_cast<const IpcHelloMessage&>(message);
|
|
||||||
ProtocolUtil::writef(&m_stream, kIpcMsgHello, hm.clientType());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kIpcCommand: {
|
|
||||||
const auto& cm = dynamic_cast<const IpcCommandMessage&>(message);
|
|
||||||
const String command = cm.command();
|
|
||||||
ProtocolUtil::writef(&m_stream, kIpcMsgCommand, &command);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG((CLOG_ERR "ipc message not supported: %d", message.type()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcLogLineMessage*
|
|
||||||
IpcServerProxy::parseLogLine()
|
|
||||||
{
|
|
||||||
String logLine;
|
|
||||||
ProtocolUtil::readf(&m_stream, kIpcMsgLogLine + 4, &logLine);
|
|
||||||
|
|
||||||
// must be deleted by event handler.
|
|
||||||
return new IpcLogLineMessage(logLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcServerProxy::disconnect()
|
|
||||||
{
|
|
||||||
LOG((CLOG_DEBUG "ipc disconnect, closing stream"));
|
|
||||||
m_stream.close();
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "base/Event.h"
|
|
||||||
#include "base/EventTypes.h"
|
|
||||||
|
|
||||||
namespace synergy { class IStream; }
|
|
||||||
class IpcMessage;
|
|
||||||
class IpcLogLineMessage;
|
|
||||||
class IEventQueue;
|
|
||||||
|
|
||||||
class IpcServerProxy {
|
|
||||||
friend class IpcClient;
|
|
||||||
|
|
||||||
public:
|
|
||||||
IpcServerProxy(synergy::IStream& stream, IEventQueue* events);
|
|
||||||
virtual ~IpcServerProxy();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void send(const IpcMessage& message);
|
|
||||||
|
|
||||||
void handleData(const Event&, void*);
|
|
||||||
IpcLogLineMessage* parseLogLine();
|
|
||||||
void disconnect();
|
|
||||||
|
|
||||||
private:
|
|
||||||
synergy::IStream& m_stream;
|
|
||||||
IEventQueue* m_events;
|
|
||||||
};
|
|
|
@ -40,7 +40,7 @@ add_library(platform STATIC ${sources})
|
||||||
target_link_libraries(platform client ${libs})
|
target_link_libraries(platform client ${libs})
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
target_link_libraries(platform io net ipc core client ${libs})
|
target_link_libraries(platform io net core client ${libs})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
|
|
@ -1,588 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* Copyright (C) 2009 Chris Schoeneman
|
|
||||||
*
|
|
||||||
* 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 LICENSE 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 "platform/MSWindowsWatchdog.h"
|
|
||||||
|
|
||||||
#include "ipc/IpcLogOutputter.h"
|
|
||||||
#include "ipc/IpcServer.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "core/App.h"
|
|
||||||
#include "core/ArgsBase.h"
|
|
||||||
#include "mt/Thread.h"
|
|
||||||
#include "arch/win32/ArchDaemonWindows.h"
|
|
||||||
#include "arch/win32/XArchWindows.h"
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
#include "base/log_outputters.h"
|
|
||||||
#include "base/TMethodJob.h"
|
|
||||||
#include "base/Log.h"
|
|
||||||
#include "common/Version.h"
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <UserEnv.h>
|
|
||||||
#include <Shellapi.h>
|
|
||||||
|
|
||||||
#define MAXIMUM_WAIT_TIME 3
|
|
||||||
enum {
|
|
||||||
kOutputBufferSize = 4096
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef VOID (WINAPI *SendSas)(BOOL asUser);
|
|
||||||
|
|
||||||
const char g_activeDesktop[] = {"activeDesktop:"};
|
|
||||||
|
|
||||||
MSWindowsWatchdog::MSWindowsWatchdog(
|
|
||||||
bool autoDetectCommand,
|
|
||||||
IpcServer& ipcServer,
|
|
||||||
IpcLogOutputter& ipcLogOutputter) :
|
|
||||||
m_thread(NULL),
|
|
||||||
m_autoDetectCommand(autoDetectCommand),
|
|
||||||
m_monitoring(true),
|
|
||||||
m_commandChanged(false),
|
|
||||||
m_stdOutWrite(NULL),
|
|
||||||
m_stdOutRead(NULL),
|
|
||||||
m_ipcServer(ipcServer),
|
|
||||||
m_ipcLogOutputter(ipcLogOutputter),
|
|
||||||
m_elevateProcess(false),
|
|
||||||
m_processFailures(0),
|
|
||||||
m_processRunning(false),
|
|
||||||
m_fileLogOutputter(NULL),
|
|
||||||
m_autoElevated(false),
|
|
||||||
m_ready(false)
|
|
||||||
{
|
|
||||||
m_mutex = ARCH->newMutex();
|
|
||||||
m_condVar = ARCH->newCondVar();
|
|
||||||
}
|
|
||||||
|
|
||||||
MSWindowsWatchdog::~MSWindowsWatchdog()
|
|
||||||
{
|
|
||||||
if (m_condVar != NULL) {
|
|
||||||
ARCH->closeCondVar(m_condVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_mutex != NULL) {
|
|
||||||
ARCH->closeMutex(m_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::startAsync()
|
|
||||||
{
|
|
||||||
m_thread = new Thread(new TMethodJob<MSWindowsWatchdog>(
|
|
||||||
this, &MSWindowsWatchdog::mainLoop, nullptr));
|
|
||||||
|
|
||||||
m_outputThread = new Thread(new TMethodJob<MSWindowsWatchdog>(
|
|
||||||
this, &MSWindowsWatchdog::outputLoop, nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::stop()
|
|
||||||
{
|
|
||||||
m_monitoring = false;
|
|
||||||
|
|
||||||
m_thread->wait(5);
|
|
||||||
delete m_thread;
|
|
||||||
|
|
||||||
m_outputThread->wait(5);
|
|
||||||
delete m_outputThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE
|
|
||||||
MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security)
|
|
||||||
{
|
|
||||||
HANDLE sourceToken;
|
|
||||||
|
|
||||||
BOOL tokenRet = OpenProcessToken(
|
|
||||||
process,
|
|
||||||
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
|
|
||||||
&sourceToken);
|
|
||||||
|
|
||||||
if (!tokenRet) {
|
|
||||||
LOG((CLOG_ERR "could not open token, process handle: %d", process));
|
|
||||||
throw XArch(new XArchEvalWindows());
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken));
|
|
||||||
|
|
||||||
HANDLE newToken;
|
|
||||||
BOOL duplicateRet = DuplicateTokenEx(
|
|
||||||
sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
|
|
||||||
SecurityImpersonation, TokenPrimary, &newToken);
|
|
||||||
|
|
||||||
if (!duplicateRet) {
|
|
||||||
LOG((CLOG_ERR "could not duplicate token %i", sourceToken));
|
|
||||||
throw XArch(new XArchEvalWindows());
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
|
|
||||||
return newToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE
|
|
||||||
MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security)
|
|
||||||
{
|
|
||||||
// always elevate if we are at the vista/7 login screen. we could also
|
|
||||||
// elevate for the uac dialog (consent.exe) but this would be pointless,
|
|
||||||
// since synergy would re-launch as non-elevated after the desk switch,
|
|
||||||
// and so would be unusable with the new elevated process taking focus.
|
|
||||||
if (m_elevateProcess
|
|
||||||
|| m_autoElevated
|
|
||||||
|| m_session.isProcessInSession("logonui.exe", NULL)) {
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "getting elevated token, %s",
|
|
||||||
(m_elevateProcess ? "elevation required" : "at login screen")));
|
|
||||||
|
|
||||||
HANDLE process;
|
|
||||||
if (!m_session.isProcessInSession("winlogon.exe", &process)) {
|
|
||||||
throw XMSWindowsWatchdogError("cannot get user token without winlogon.exe");
|
|
||||||
}
|
|
||||||
|
|
||||||
return duplicateProcessToken(process, security);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG((CLOG_DEBUG "getting non-elevated token"));
|
|
||||||
return m_session.getUserToken(security);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::mainLoop(void*)
|
|
||||||
{
|
|
||||||
shutdownExistingProcesses();
|
|
||||||
|
|
||||||
SendSas sendSasFunc = NULL;
|
|
||||||
HINSTANCE sasLib = LoadLibrary("sas.dll");
|
|
||||||
if (sasLib) {
|
|
||||||
LOG((CLOG_DEBUG "found sas.dll"));
|
|
||||||
sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECURITY_ATTRIBUTES saAttr;
|
|
||||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
||||||
saAttr.bInheritHandle = TRUE;
|
|
||||||
saAttr.lpSecurityDescriptor = NULL;
|
|
||||||
|
|
||||||
if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) {
|
|
||||||
throw XArch(new XArchEvalWindows());
|
|
||||||
}
|
|
||||||
|
|
||||||
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
|
|
||||||
|
|
||||||
while (m_monitoring) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (m_processRunning && getCommand().empty()) {
|
|
||||||
LOG((CLOG_INFO "process started but command is empty, shutting down"));
|
|
||||||
shutdownExistingProcesses();
|
|
||||||
m_processRunning = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_processFailures != 0) {
|
|
||||||
// increasing backoff period, maximum of 10 seconds.
|
|
||||||
int timeout = (m_processFailures * 2) < 10 ? (m_processFailures * 2) : 10;
|
|
||||||
LOG((CLOG_INFO "backing off, wait=%ds, failures=%d", timeout, m_processFailures));
|
|
||||||
ARCH->sleep(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getCommand().empty() && ((m_processFailures != 0) || m_session.hasChanged() || m_commandChanged)) {
|
|
||||||
startProcess();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_processRunning && !isProcessActive()) {
|
|
||||||
|
|
||||||
m_processFailures++;
|
|
||||||
m_processRunning = false;
|
|
||||||
|
|
||||||
LOG((CLOG_WARN "detected application not running, pid=%d",
|
|
||||||
m_processInfo.dwProcessId));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sendSasFunc != NULL) {
|
|
||||||
|
|
||||||
HANDLE sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS");
|
|
||||||
if (sendSasEvent != NULL) {
|
|
||||||
|
|
||||||
// use SendSAS event to wait for next session (timeout 1 second).
|
|
||||||
if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0) {
|
|
||||||
LOG((CLOG_DEBUG "calling SendSAS"));
|
|
||||||
sendSasFunc(FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(sendSasEvent);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the sas event failed, wait by sleeping.
|
|
||||||
ARCH->sleep(1);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (std::exception& e) {
|
|
||||||
LOG((CLOG_ERR "failed to launch, error: %s", e.what()));
|
|
||||||
m_processFailures++;
|
|
||||||
m_processRunning = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
LOG((CLOG_ERR "failed to launch, unknown error."));
|
|
||||||
m_processFailures++;
|
|
||||||
m_processRunning = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_processRunning) {
|
|
||||||
LOG((CLOG_DEBUG "terminated running process on exit"));
|
|
||||||
shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "watchdog main thread finished"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
MSWindowsWatchdog::isProcessActive()
|
|
||||||
{
|
|
||||||
DWORD exitCode;
|
|
||||||
GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
|
|
||||||
return exitCode == STILL_ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::setFileLogOutputter(FileLogOutputter* outputter)
|
|
||||||
{
|
|
||||||
m_fileLogOutputter = outputter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::startProcess()
|
|
||||||
{
|
|
||||||
if (m_command.empty()) {
|
|
||||||
throw XMSWindowsWatchdogError("cannot start process, command is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_commandChanged = false;
|
|
||||||
|
|
||||||
if (m_processRunning) {
|
|
||||||
LOG((CLOG_DEBUG "closing existing process to make way for new one"));
|
|
||||||
shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20);
|
|
||||||
m_processRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_session.updateActiveSession();
|
|
||||||
|
|
||||||
SECURITY_ATTRIBUTES sa;
|
|
||||||
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
|
||||||
|
|
||||||
getActiveDesktop(&sa);
|
|
||||||
|
|
||||||
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
|
||||||
HANDLE userToken = getUserToken(&sa);
|
|
||||||
m_elevateProcess = m_autoElevated ? m_autoElevated : m_elevateProcess;
|
|
||||||
m_autoElevated = false;
|
|
||||||
|
|
||||||
// patch by Jack Zhou and Henry Tung
|
|
||||||
// set UIAccess to fix Windows 8 GUI interaction
|
|
||||||
// http://symless.com/spit/issues/details/3338/#c70
|
|
||||||
DWORD uiAccess = 1;
|
|
||||||
SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD));
|
|
||||||
|
|
||||||
BOOL createRet = doStartProcess(m_command, userToken, &sa);
|
|
||||||
|
|
||||||
if (!createRet) {
|
|
||||||
LOG((CLOG_ERR "could not launch"));
|
|
||||||
DWORD exitCode = 0;
|
|
||||||
GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
|
|
||||||
LOG((CLOG_ERR "exit code: %d", exitCode));
|
|
||||||
throw XArch(new XArchEvalWindows);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// wait for program to fail.
|
|
||||||
ARCH->sleep(1);
|
|
||||||
if (!isProcessActive()) {
|
|
||||||
throw XMSWindowsWatchdogError("process immediately stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_processRunning = true;
|
|
||||||
m_processFailures = 0;
|
|
||||||
|
|
||||||
LOG((CLOG_DEBUG "started process, session=%i, elevated: %s, command=%s",
|
|
||||||
m_session.getActiveSessionId(),
|
|
||||||
m_elevateProcess ? "yes" : "no",
|
|
||||||
m_command.c_str()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL
|
|
||||||
MSWindowsWatchdog::doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
|
|
||||||
{
|
|
||||||
// clear, as we're reusing process info struct
|
|
||||||
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
|
|
||||||
|
|
||||||
STARTUPINFO si;
|
|
||||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
|
||||||
si.cb = sizeof(STARTUPINFO);
|
|
||||||
si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe?
|
|
||||||
si.hStdError = m_stdOutWrite;
|
|
||||||
si.hStdOutput = m_stdOutWrite;
|
|
||||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
|
||||||
|
|
||||||
LPVOID environment;
|
|
||||||
BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
|
|
||||||
if (!blockRet) {
|
|
||||||
LOG((CLOG_ERR "could not create environment block"));
|
|
||||||
throw XArch(new XArchEvalWindows);
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD creationFlags =
|
|
||||||
NORMAL_PRIORITY_CLASS |
|
|
||||||
CREATE_NO_WINDOW |
|
|
||||||
CREATE_UNICODE_ENVIRONMENT;
|
|
||||||
|
|
||||||
// re-launch in current active user session
|
|
||||||
LOG((CLOG_INFO "starting new process"));
|
|
||||||
BOOL createRet = CreateProcessAsUser(
|
|
||||||
userToken, NULL, LPSTR(command.c_str()),
|
|
||||||
sa, NULL, TRUE, creationFlags,
|
|
||||||
environment, NULL, &si, &m_processInfo);
|
|
||||||
|
|
||||||
DestroyEnvironmentBlock(environment);
|
|
||||||
CloseHandle(userToken);
|
|
||||||
|
|
||||||
return createRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::setCommand(const std::string& command, bool elevate)
|
|
||||||
{
|
|
||||||
LOG((CLOG_INFO "service command updated"));
|
|
||||||
m_command = command;
|
|
||||||
m_elevateProcess = elevate;
|
|
||||||
m_commandChanged = true;
|
|
||||||
m_processFailures = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
MSWindowsWatchdog::getCommand() const
|
|
||||||
{
|
|
||||||
if (!m_autoDetectCommand) {
|
|
||||||
return m_command;
|
|
||||||
}
|
|
||||||
|
|
||||||
// seems like a fairly convoluted way to get the process name
|
|
||||||
const char* launchName = App::instance().argsBase().m_pname;
|
|
||||||
std::string args = ARCH->commandLine();
|
|
||||||
|
|
||||||
// build up a full command line
|
|
||||||
std::stringstream cmdTemp;
|
|
||||||
cmdTemp << launchName << args;
|
|
||||||
|
|
||||||
std::string cmd = cmdTemp.str();
|
|
||||||
|
|
||||||
size_t i;
|
|
||||||
std::string find = "--relaunch";
|
|
||||||
while ((i = cmd.find(find)) != std::string::npos) {
|
|
||||||
cmd.replace(i, find.length(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::outputLoop(void*)
|
|
||||||
{
|
|
||||||
// +1 char for \0
|
|
||||||
CHAR buffer[kOutputBufferSize + 1];
|
|
||||||
|
|
||||||
while (m_monitoring) {
|
|
||||||
|
|
||||||
DWORD bytesRead;
|
|
||||||
BOOL success = ReadFile(m_stdOutRead, buffer, kOutputBufferSize, &bytesRead, NULL);
|
|
||||||
|
|
||||||
// assume the process has gone away? slow down
|
|
||||||
// the reads until another one turns up.
|
|
||||||
if (!success || bytesRead == 0) {
|
|
||||||
ARCH->sleep(1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buffer[bytesRead] = '\0';
|
|
||||||
|
|
||||||
testOutput(buffer);
|
|
||||||
|
|
||||||
m_ipcLogOutputter.write(kINFO, buffer);
|
|
||||||
|
|
||||||
if (m_fileLogOutputter != NULL) {
|
|
||||||
m_fileLogOutputter->write(kINFO, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::shutdownProcess(HANDLE handle, DWORD pid, int timeout)
|
|
||||||
{
|
|
||||||
DWORD exitCode;
|
|
||||||
GetExitCodeProcess(handle, &exitCode);
|
|
||||||
if (exitCode != STILL_ACTIVE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcShutdownMessage shutdown;
|
|
||||||
m_ipcServer.send(shutdown, kIpcClientNode);
|
|
||||||
|
|
||||||
// wait for process to exit gracefully.
|
|
||||||
double start = ARCH->time();
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
GetExitCodeProcess(handle, &exitCode);
|
|
||||||
if (exitCode != STILL_ACTIVE) {
|
|
||||||
// yay, we got a graceful shutdown. there should be no hook in use errors!
|
|
||||||
LOG((CLOG_INFO "process %d was shutdown gracefully", pid));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
double elapsed = (ARCH->time() - start);
|
|
||||||
if (elapsed > timeout) {
|
|
||||||
// if timeout reached, kill forcefully.
|
|
||||||
// calling TerminateProcess on synergy is very bad!
|
|
||||||
// it causes the hook DLL to stay loaded in some apps,
|
|
||||||
// making it impossible to start synergy again.
|
|
||||||
LOG((CLOG_WARN "shutdown timed out after %d secs, forcefully terminating", (int)elapsed));
|
|
||||||
TerminateProcess(handle, kExitSuccess);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARCH->sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::shutdownExistingProcesses()
|
|
||||||
{
|
|
||||||
// first we need to take a snapshot of the running processes
|
|
||||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
||||||
if (snapshot == INVALID_HANDLE_VALUE) {
|
|
||||||
LOG((CLOG_ERR "could not get process snapshot"));
|
|
||||||
throw XArch(new XArchEvalWindows);
|
|
||||||
}
|
|
||||||
|
|
||||||
PROCESSENTRY32 entry;
|
|
||||||
entry.dwSize = sizeof(PROCESSENTRY32);
|
|
||||||
|
|
||||||
// get the first process, and if we can't do that then it's
|
|
||||||
// unlikely we can go any further
|
|
||||||
BOOL gotEntry = Process32First(snapshot, &entry);
|
|
||||||
if (!gotEntry) {
|
|
||||||
LOG((CLOG_ERR "could not get first process entry"));
|
|
||||||
throw XArch(new XArchEvalWindows);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now just iterate until we can find winlogon.exe pid
|
|
||||||
DWORD pid = 0;
|
|
||||||
while (gotEntry) {
|
|
||||||
|
|
||||||
// make sure we're not checking the system process
|
|
||||||
if (entry.th32ProcessID != 0) {
|
|
||||||
|
|
||||||
if (_stricmp(entry.szExeFile, "synergyc.exe") == 0 ||
|
|
||||||
_stricmp(entry.szExeFile, "synergys.exe") == 0) {
|
|
||||||
|
|
||||||
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
|
|
||||||
shutdownProcess(handle, entry.th32ProcessID, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now move on to the next entry (if we're not at the end)
|
|
||||||
gotEntry = Process32Next(snapshot, &entry);
|
|
||||||
if (!gotEntry) {
|
|
||||||
|
|
||||||
DWORD err = GetLastError();
|
|
||||||
if (err != ERROR_NO_MORE_FILES) {
|
|
||||||
|
|
||||||
// only worry about error if it's not the end of the snapshot
|
|
||||||
LOG((CLOG_ERR "could not get subsiquent process entry"));
|
|
||||||
throw XArch(new XArchEvalWindows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(snapshot);
|
|
||||||
m_processRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security)
|
|
||||||
{
|
|
||||||
String installedDir = ARCH->getInstalledDirectory();
|
|
||||||
if (!installedDir.empty()) {
|
|
||||||
String syntoolCommand;
|
|
||||||
syntoolCommand.append("\"").append(installedDir).append("\\").append("syntool").append("\"");
|
|
||||||
syntoolCommand.append(" --get-active-desktop");
|
|
||||||
|
|
||||||
m_session.updateActiveSession();
|
|
||||||
bool elevateProcess = m_elevateProcess;
|
|
||||||
m_elevateProcess = true;
|
|
||||||
HANDLE userToken = getUserToken(security);
|
|
||||||
m_elevateProcess = elevateProcess;
|
|
||||||
|
|
||||||
BOOL createRet = doStartProcess(syntoolCommand, userToken, security);
|
|
||||||
|
|
||||||
if (!createRet) {
|
|
||||||
DWORD rc = GetLastError();
|
|
||||||
RevertToSelf();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG((CLOG_DEBUG "launched syntool to check active desktop"));
|
|
||||||
}
|
|
||||||
|
|
||||||
ARCH->lockMutex(m_mutex);
|
|
||||||
int waitTime = 0;
|
|
||||||
while (!m_ready) {
|
|
||||||
if (waitTime >= MAXIMUM_WAIT_TIME) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARCH->waitCondVar(m_condVar, m_mutex, 1.0);
|
|
||||||
waitTime++;
|
|
||||||
}
|
|
||||||
m_ready = false;
|
|
||||||
ARCH->unlockMutex(m_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsWatchdog::testOutput(String buffer)
|
|
||||||
{
|
|
||||||
// HACK: check standard output seems hacky.
|
|
||||||
size_t i = buffer.find(g_activeDesktop);
|
|
||||||
if (i != String::npos) {
|
|
||||||
size_t s = sizeof(g_activeDesktop);
|
|
||||||
String defaultDesktop("Default");
|
|
||||||
String sub = buffer.substr(i + s - 1, defaultDesktop.size());
|
|
||||||
if (sub != defaultDesktop) {
|
|
||||||
m_autoElevated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ARCH->lockMutex(m_mutex);
|
|
||||||
m_ready = true;
|
|
||||||
ARCH->broadcastCondVar(m_condVar);
|
|
||||||
ARCH->unlockMutex(m_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* Copyright (C) 2009 Chris Schoeneman
|
|
||||||
*
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "platform/MSWindowsSession.h"
|
|
||||||
#include "core/XSynergy.h"
|
|
||||||
#include "arch/IArchMultithread.h"
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
class Thread;
|
|
||||||
class IpcLogOutputter;
|
|
||||||
class IpcServer;
|
|
||||||
class FileLogOutputter;
|
|
||||||
|
|
||||||
class MSWindowsWatchdog {
|
|
||||||
public:
|
|
||||||
MSWindowsWatchdog(
|
|
||||||
bool autoDetectCommand,
|
|
||||||
IpcServer& ipcServer,
|
|
||||||
IpcLogOutputter& ipcLogOutputter);
|
|
||||||
virtual ~MSWindowsWatchdog();
|
|
||||||
|
|
||||||
void startAsync();
|
|
||||||
std::string getCommand() const;
|
|
||||||
void setCommand(const std::string& command, bool elevate);
|
|
||||||
void stop();
|
|
||||||
bool isProcessActive();
|
|
||||||
void setFileLogOutputter(FileLogOutputter* outputter);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void mainLoop(void*);
|
|
||||||
void outputLoop(void*);
|
|
||||||
void shutdownProcess(HANDLE handle, DWORD pid, int timeout);
|
|
||||||
void shutdownExistingProcesses();
|
|
||||||
HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security);
|
|
||||||
HANDLE getUserToken(LPSECURITY_ATTRIBUTES security);
|
|
||||||
void startProcess();
|
|
||||||
BOOL doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa);
|
|
||||||
void sendSas();
|
|
||||||
void getActiveDesktop(LPSECURITY_ATTRIBUTES security);
|
|
||||||
void testOutput(String buffer);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Thread* m_thread;
|
|
||||||
bool m_autoDetectCommand;
|
|
||||||
std::string m_command;
|
|
||||||
bool m_monitoring;
|
|
||||||
bool m_commandChanged;
|
|
||||||
HANDLE m_stdOutWrite;
|
|
||||||
HANDLE m_stdOutRead;
|
|
||||||
Thread* m_outputThread;
|
|
||||||
IpcServer& m_ipcServer;
|
|
||||||
IpcLogOutputter& m_ipcLogOutputter;
|
|
||||||
bool m_elevateProcess;
|
|
||||||
MSWindowsSession m_session;
|
|
||||||
PROCESS_INFORMATION m_processInfo;
|
|
||||||
int m_processFailures;
|
|
||||||
bool m_processRunning;
|
|
||||||
FileLogOutputter* m_fileLogOutputter;
|
|
||||||
bool m_autoElevated;
|
|
||||||
ArchMutex m_mutex;
|
|
||||||
ArchCond m_condVar;
|
|
||||||
bool m_ready;
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Relauncher error
|
|
||||||
/*!
|
|
||||||
An error occured in the process watchdog.
|
|
||||||
*/
|
|
||||||
class XMSWindowsWatchdogError : public XSynergy {
|
|
||||||
public:
|
|
||||||
XMSWindowsWatchdogError(const String& msg) : XSynergy(msg) { }
|
|
||||||
|
|
||||||
// XBase overrides
|
|
||||||
virtual String getWhat() const throw() { return what(); }
|
|
||||||
};
|
|
|
@ -68,4 +68,4 @@ endif()
|
||||||
|
|
||||||
add_executable(integtests ${sources})
|
add_executable(integtests ${sources})
|
||||||
target_link_libraries(integtests
|
target_link_libraries(integtests
|
||||||
arch base client common io ipc mt net platform server core gtest gmock ${libs})
|
arch base client common io mt net platform server core gtest gmock ${libs})
|
||||||
|
|
|
@ -1,205 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO(andrew): fix, tests failing intermittently on mac.
|
|
||||||
#ifndef WINAPI_CARBON
|
|
||||||
|
|
||||||
#define TEST_ENV
|
|
||||||
|
|
||||||
#include "test/global/TestEventQueue.h"
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
#include "base/EventQueue.h"
|
|
||||||
#include "base/Log.h"
|
|
||||||
#include "base/String.h"
|
|
||||||
#include "base/TMethodEventJob.h"
|
|
||||||
#include "base/TMethodJob.h"
|
|
||||||
#include "ipc/Ipc.h"
|
|
||||||
#include "ipc/IpcClient.h"
|
|
||||||
#include "ipc/IpcClientProxy.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "ipc/IpcServer.h"
|
|
||||||
#include "ipc/IpcServerProxy.h"
|
|
||||||
#include "mt/Thread.h"
|
|
||||||
#include "net/SocketMultiplexer.h"
|
|
||||||
|
|
||||||
#include "test/global/gtest.h"
|
|
||||||
|
|
||||||
#define TEST_IPC_PORT 24802
|
|
||||||
|
|
||||||
class IpcTests : public ::testing::Test
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IpcTests();
|
|
||||||
~IpcTests() override;
|
|
||||||
|
|
||||||
void connectToServer_handleMessageReceived(const Event& /*e*/, void* /*unused*/);
|
|
||||||
void sendMessageToServer_serverHandleMessageReceived(const Event& /*e*/, void* /*unused*/);
|
|
||||||
void sendMessageToClient_serverHandleClientConnected(const Event& /*e*/, void* /*unused*/);
|
|
||||||
void sendMessageToClient_clientHandleMessageReceived(const Event& /*e*/, void* /*unused*/);
|
|
||||||
|
|
||||||
public:
|
|
||||||
SocketMultiplexer m_multiplexer;
|
|
||||||
bool m_connectToServer_helloMessageReceived{false};
|
|
||||||
bool m_connectToServer_hasClientNode{false};
|
|
||||||
IpcServer* m_connectToServer_server{nullptr};
|
|
||||||
String m_sendMessageToServer_receivedString;
|
|
||||||
String m_sendMessageToClient_receivedString;
|
|
||||||
IpcClient* m_sendMessageToServer_client{nullptr};
|
|
||||||
IpcServer* m_sendMessageToClient_server{nullptr};
|
|
||||||
TestEventQueue m_events;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(IpcTests, connectToServer)
|
|
||||||
{
|
|
||||||
SocketMultiplexer socketMultiplexer;
|
|
||||||
IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
|
|
||||||
server.listen();
|
|
||||||
m_connectToServer_server = &server;
|
|
||||||
|
|
||||||
m_events.adoptHandler(
|
|
||||||
m_events.forIpcServer().messageReceived(), &server,
|
|
||||||
new TMethodEventJob<IpcTests>(
|
|
||||||
this, &IpcTests::connectToServer_handleMessageReceived));
|
|
||||||
|
|
||||||
IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
|
|
||||||
client.connect();
|
|
||||||
|
|
||||||
m_events.initQuitTimeout(5);
|
|
||||||
m_events.loop();
|
|
||||||
m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server);
|
|
||||||
m_events.cleanupQuitTimeout();
|
|
||||||
|
|
||||||
EXPECT_EQ(true, m_connectToServer_helloMessageReceived);
|
|
||||||
EXPECT_EQ(true, m_connectToServer_hasClientNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(IpcTests, sendMessageToServer)
|
|
||||||
{
|
|
||||||
SocketMultiplexer socketMultiplexer;
|
|
||||||
IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
|
|
||||||
server.listen();
|
|
||||||
|
|
||||||
// event handler sends "test" command to server.
|
|
||||||
m_events.adoptHandler(
|
|
||||||
m_events.forIpcServer().messageReceived(), &server,
|
|
||||||
new TMethodEventJob<IpcTests>(
|
|
||||||
this, &IpcTests::sendMessageToServer_serverHandleMessageReceived));
|
|
||||||
|
|
||||||
IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
|
|
||||||
client.connect();
|
|
||||||
m_sendMessageToServer_client = &client;
|
|
||||||
|
|
||||||
m_events.initQuitTimeout(5);
|
|
||||||
m_events.loop();
|
|
||||||
m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server);
|
|
||||||
m_events.cleanupQuitTimeout();
|
|
||||||
|
|
||||||
EXPECT_EQ("test", m_sendMessageToServer_receivedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(IpcTests, sendMessageToClient)
|
|
||||||
{
|
|
||||||
SocketMultiplexer socketMultiplexer;
|
|
||||||
IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
|
|
||||||
server.listen();
|
|
||||||
m_sendMessageToClient_server = &server;
|
|
||||||
|
|
||||||
// event handler sends "test" log line to client.
|
|
||||||
m_events.adoptHandler(
|
|
||||||
m_events.forIpcServer().messageReceived(), &server,
|
|
||||||
new TMethodEventJob<IpcTests>(
|
|
||||||
this, &IpcTests::sendMessageToClient_serverHandleClientConnected));
|
|
||||||
|
|
||||||
IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
|
|
||||||
client.connect();
|
|
||||||
|
|
||||||
m_events.adoptHandler(
|
|
||||||
m_events.forIpcClient().messageReceived(), &client,
|
|
||||||
new TMethodEventJob<IpcTests>(
|
|
||||||
this, &IpcTests::sendMessageToClient_clientHandleMessageReceived));
|
|
||||||
|
|
||||||
m_events.initQuitTimeout(5);
|
|
||||||
m_events.loop();
|
|
||||||
m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server);
|
|
||||||
m_events.removeHandler(m_events.forIpcClient().messageReceived(), &client);
|
|
||||||
m_events.cleanupQuitTimeout();
|
|
||||||
|
|
||||||
EXPECT_EQ("test", m_sendMessageToClient_receivedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcTests::IpcTests()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcTests::~IpcTests()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcTests::connectToServer_handleMessageReceived(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* m = dynamic_cast<IpcMessage*>(e.getDataObject());
|
|
||||||
if (m->type() == kIpcHello) {
|
|
||||||
m_connectToServer_hasClientNode =
|
|
||||||
m_connectToServer_server->hasClients(kIpcClientNode);
|
|
||||||
m_connectToServer_helloMessageReceived = true;
|
|
||||||
m_events.raiseQuitEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcTests::sendMessageToServer_serverHandleMessageReceived(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* m = dynamic_cast<IpcMessage*>(e.getDataObject());
|
|
||||||
if (m->type() == kIpcHello) {
|
|
||||||
LOG((CLOG_DEBUG "client said hello, sending test to server"));
|
|
||||||
IpcCommandMessage m("test", true);
|
|
||||||
m_sendMessageToServer_client->send(m);
|
|
||||||
}
|
|
||||||
else if (m->type() == kIpcCommand) {
|
|
||||||
auto* cm = dynamic_cast<IpcCommandMessage*>(m);
|
|
||||||
LOG((CLOG_DEBUG "got ipc command message, %d", cm->command().c_str()));
|
|
||||||
m_sendMessageToServer_receivedString = cm->command();
|
|
||||||
m_events.raiseQuitEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcTests::sendMessageToClient_serverHandleClientConnected(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* m = dynamic_cast<IpcMessage*>(e.getDataObject());
|
|
||||||
if (m->type() == kIpcHello) {
|
|
||||||
LOG((CLOG_DEBUG "client said hello, sending test to client"));
|
|
||||||
IpcLogLineMessage m("test");
|
|
||||||
m_sendMessageToClient_server->send(m, kIpcClientNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IpcTests::sendMessageToClient_clientHandleMessageReceived(const Event& e, void* /*unused*/)
|
|
||||||
{
|
|
||||||
auto* m = dynamic_cast<IpcMessage*>(e.getDataObject());
|
|
||||||
if (m->type() == kIpcLogLine) {
|
|
||||||
auto* llm = dynamic_cast<IpcLogLineMessage*>(m);
|
|
||||||
LOG((CLOG_DEBUG "got ipc log message, %d", llm->logLine().c_str()));
|
|
||||||
m_sendMessageToClient_receivedString = llm->logLine();
|
|
||||||
m_events.raiseQuitEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WINAPI_CARBON
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2015-2016 Symless Ltd.
|
|
||||||
*
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ipc/IpcServer.h"
|
|
||||||
#include "ipc/IpcMessage.h"
|
|
||||||
#include "arch/Arch.h"
|
|
||||||
|
|
||||||
#include "test/global/gmock.h"
|
|
||||||
|
|
||||||
using ::testing::_;
|
|
||||||
using ::testing::Invoke;
|
|
||||||
|
|
||||||
class IEventQueue;
|
|
||||||
|
|
||||||
class MockIpcServer : public IpcServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MockIpcServer() :
|
|
||||||
m_sendCond(ARCH->newCondVar()),
|
|
||||||
m_sendMutex(ARCH->newMutex()) { }
|
|
||||||
|
|
||||||
~MockIpcServer() {
|
|
||||||
if (m_sendCond != NULL) {
|
|
||||||
ARCH->closeCondVar(m_sendCond);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_sendMutex != NULL) {
|
|
||||||
ARCH->closeMutex(m_sendMutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MOCK_METHOD0(listen, void());
|
|
||||||
MOCK_METHOD2(send, void(const IpcMessage&, EIpcClientType));
|
|
||||||
MOCK_CONST_METHOD1(hasClients, bool(EIpcClientType));
|
|
||||||
|
|
||||||
void delegateToFake() {
|
|
||||||
ON_CALL(*this, send(_, _)).WillByDefault(Invoke(this, &MockIpcServer::mockSend));
|
|
||||||
}
|
|
||||||
|
|
||||||
void waitForSend() {
|
|
||||||
ARCH->waitCondVar(m_sendCond, m_sendMutex, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void mockSend(const IpcMessage&, EIpcClientType) {
|
|
||||||
ArchMutexLock lock(m_sendMutex);
|
|
||||||
ARCH->broadcastCondVar(m_sendCond);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArchCond m_sendCond;
|
|
||||||
ArchMutex m_sendMutex;
|
|
||||||
};
|
|
|
@ -45,10 +45,6 @@ public:
|
||||||
MOCK_METHOD0(getSystemTarget, void*());
|
MOCK_METHOD0(getSystemTarget, void*());
|
||||||
MOCK_METHOD0(forClient, ClientEvents&());
|
MOCK_METHOD0(forClient, ClientEvents&());
|
||||||
MOCK_METHOD0(forIStream, IStreamEvents&());
|
MOCK_METHOD0(forIStream, IStreamEvents&());
|
||||||
MOCK_METHOD0(forIpcClient, IpcClientEvents&());
|
|
||||||
MOCK_METHOD0(forIpcClientProxy, IpcClientProxyEvents&());
|
|
||||||
MOCK_METHOD0(forIpcServer, IpcServerEvents&());
|
|
||||||
MOCK_METHOD0(forIpcServerProxy, IpcServerProxyEvents&());
|
|
||||||
MOCK_METHOD0(forIDataSocket, IDataSocketEvents&());
|
MOCK_METHOD0(forIDataSocket, IDataSocketEvents&());
|
||||||
MOCK_METHOD0(forIListenSocket, IListenSocketEvents&());
|
MOCK_METHOD0(forIListenSocket, IListenSocketEvents&());
|
||||||
MOCK_METHOD0(forISocket, ISocketEvents&());
|
MOCK_METHOD0(forISocket, ISocketEvents&());
|
||||||
|
|
|
@ -68,4 +68,4 @@ endif()
|
||||||
|
|
||||||
add_executable(unittests ${sources})
|
add_executable(unittests ${sources})
|
||||||
target_link_libraries(unittests
|
target_link_libraries(unittests
|
||||||
arch base client server common io net platform server core mt ipc gtest gmock shared ${libs})
|
arch base client server common io net platform server core mt gtest gmock shared ${libs})
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
/*
|
|
||||||
* synergy -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2015-2016 Symless Ltd.
|
|
||||||
*
|
|
||||||
* 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 LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define TEST_ENV
|
|
||||||
|
|
||||||
#include "test/mock/ipc/MockIpcServer.h"
|
|
||||||
|
|
||||||
#include "base/String.h"
|
|
||||||
#include "common/common.h"
|
|
||||||
#include "ipc/IpcLogOutputter.h"
|
|
||||||
#include "mt/Thread.h"
|
|
||||||
|
|
||||||
#include "test/global/gmock.h"
|
|
||||||
#include "test/global/gtest.h"
|
|
||||||
|
|
||||||
// HACK: ipc logging only used on windows anyway
|
|
||||||
#if WINAPI_MSWINDOWS
|
|
||||||
|
|
||||||
using ::testing::_;
|
|
||||||
using ::testing::Return;
|
|
||||||
using ::testing::Matcher;
|
|
||||||
using ::testing::MatcherCast;
|
|
||||||
using ::testing::Property;
|
|
||||||
using ::testing::StrEq;
|
|
||||||
using ::testing::AtLeast;
|
|
||||||
|
|
||||||
using namespace synergy;
|
|
||||||
|
|
||||||
inline const Matcher<const IpcMessage&> IpcLogLineMessageEq(const String& s) {
|
|
||||||
const Matcher<const IpcLogLineMessage&> m(
|
|
||||||
Property(&IpcLogLineMessage::logLine, StrEq(s)));
|
|
||||||
return MatcherCast<const IpcMessage&>(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent)
|
|
||||||
{
|
|
||||||
MockIpcServer mockServer;
|
|
||||||
mockServer.delegateToFake();
|
|
||||||
|
|
||||||
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(mockServer, hasClients(_)).Times(AtLeast(3));
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1);
|
|
||||||
|
|
||||||
IpcLogOutputter outputter(mockServer, kIpcClientUnknown, true);
|
|
||||||
outputter.write(kNOTE, "mock 1");
|
|
||||||
mockServer.waitForSend();
|
|
||||||
outputter.write(kNOTE, "mock 2");
|
|
||||||
mockServer.waitForSend();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated)
|
|
||||||
{
|
|
||||||
MockIpcServer mockServer;
|
|
||||||
|
|
||||||
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
|
|
||||||
EXPECT_CALL(mockServer, hasClients(_)).Times(1);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\nmock 3\n"), _)).Times(1);
|
|
||||||
|
|
||||||
IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false);
|
|
||||||
outputter.bufferMaxSize(2);
|
|
||||||
|
|
||||||
// log more lines than the buffer can contain
|
|
||||||
outputter.write(kNOTE, "mock 1");
|
|
||||||
outputter.write(kNOTE, "mock 2");
|
|
||||||
outputter.write(kNOTE, "mock 3");
|
|
||||||
outputter.sendBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent)
|
|
||||||
{
|
|
||||||
MockIpcServer mockServer;
|
|
||||||
|
|
||||||
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(mockServer, hasClients(_)).Times(1);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1);
|
|
||||||
|
|
||||||
IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false);
|
|
||||||
outputter.bufferMaxSize(2);
|
|
||||||
|
|
||||||
// log more lines than the buffer can contain
|
|
||||||
outputter.write(kNOTE, "mock 1");
|
|
||||||
outputter.write(kNOTE, "mock 2");
|
|
||||||
outputter.sendBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: temporarily disable this intermittently failing unit test.
|
|
||||||
// when the build machine is under heavy load, a race condition
|
|
||||||
// usually happens.
|
|
||||||
#if 0
|
|
||||||
TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated)
|
|
||||||
{
|
|
||||||
MockIpcServer mockServer;
|
|
||||||
|
|
||||||
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(mockServer, hasClients(_)).Times(2);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 4\nmock 5\n"), _)).Times(1);
|
|
||||||
|
|
||||||
IpcLogOutputter outputter(mockServer, false);
|
|
||||||
outputter.bufferRateLimit(2, 1); // 1s
|
|
||||||
|
|
||||||
// log 1 more line than the buffer can accept in time limit.
|
|
||||||
outputter.write(kNOTE, "mock 1");
|
|
||||||
outputter.write(kNOTE, "mock 2");
|
|
||||||
outputter.write(kNOTE, "mock 3");
|
|
||||||
|
|
||||||
outputter.sendBuffer();
|
|
||||||
|
|
||||||
// after waiting the time limit send another to make sure
|
|
||||||
// we can log after the time limit passes.
|
|
||||||
// HACK: sleep causes the unit test to fail intermittently,
|
|
||||||
// so lets try 100ms (there must be a better way to solve this)
|
|
||||||
ARCH->sleep(2); // 2s
|
|
||||||
outputter.write(kNOTE, "mock 4");
|
|
||||||
outputter.write(kNOTE, "mock 5");
|
|
||||||
outputter.write(kNOTE, "mock 6");
|
|
||||||
|
|
||||||
outputter.sendBuffer();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent)
|
|
||||||
{
|
|
||||||
MockIpcServer mockServer;
|
|
||||||
|
|
||||||
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
|
|
||||||
|
|
||||||
EXPECT_CALL(mockServer, hasClients(_)).Times(2);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1);
|
|
||||||
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 3\nmock 4\n"), _)).Times(1);
|
|
||||||
|
|
||||||
IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false);
|
|
||||||
outputter.bufferRateLimit(4, 1); // 1s (should be plenty of time)
|
|
||||||
|
|
||||||
// log 1 more line than the buffer can accept in time limit.
|
|
||||||
outputter.write(kNOTE, "mock 1");
|
|
||||||
outputter.write(kNOTE, "mock 2");
|
|
||||||
outputter.sendBuffer();
|
|
||||||
|
|
||||||
// after waiting the time limit send another to make sure
|
|
||||||
// we can log after the time limit passes.
|
|
||||||
outputter.write(kNOTE, "mock 3");
|
|
||||||
outputter.write(kNOTE, "mock 4");
|
|
||||||
outputter.sendBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WINAPI_MSWINDOWS
|
|
|
@ -260,22 +260,6 @@ TEST(GenericArgsParsingTests, parseGenericArgs_noTrayCmd_disableTrayTrue)
|
||||||
EXPECT_EQ(1, i);
|
EXPECT_EQ(1, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(GenericArgsParsingTests, parseGenericArgs_ipcCmd_enableIpcTrue)
|
|
||||||
{
|
|
||||||
int i = 1;
|
|
||||||
const int argc = 2;
|
|
||||||
const char* kIpcCmd[argc] = { "stub", "--ipc" };
|
|
||||||
|
|
||||||
ArgParser argParser(nullptr);
|
|
||||||
ArgsBase argsBase;
|
|
||||||
argParser.setArgsBase(argsBase);
|
|
||||||
|
|
||||||
argParser.parseGenericArgs(argc, kIpcCmd, i);
|
|
||||||
|
|
||||||
EXPECT_EQ(true, argsBase.m_enableIpc);
|
|
||||||
EXPECT_EQ(1, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef WINAPI_XWINDOWS
|
#ifndef WINAPI_XWINDOWS
|
||||||
TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnNonLinux_enableDragDropTrue)
|
TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnNonLinux_enableDragDropTrue)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue