Remove IPC
This commit is contained in:
parent
607201d586
commit
5ff0637a82
|
@ -322,6 +322,6 @@ add_subdirectory (src)
|
|||
|
||||
if (SYNERGY_TIDY)
|
||||
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}")
|
||||
endif()
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
add_executable(synergy-core main.cpp)
|
||||
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 (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
|
|
|
@ -39,4 +39,4 @@ endif()
|
|||
|
||||
add_executable(synergyc ${sources})
|
||||
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})
|
||||
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(common)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(mt)
|
||||
add_subdirectory(net)
|
||||
add_subdirectory(platform)
|
||||
|
|
|
@ -30,10 +30,6 @@
|
|||
|
||||
EVENT_TYPE_ACCESSOR(Client)
|
||||
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(IListenSocket)
|
||||
EVENT_TYPE_ACCESSOR(ISocket)
|
||||
|
@ -68,10 +64,6 @@ EventQueue::EventQueue() :
|
|||
m_nextType(Event::kLast),
|
||||
m_typesForClient(nullptr),
|
||||
m_typesForIStream(nullptr),
|
||||
m_typesForIpcClient(nullptr),
|
||||
m_typesForIpcClientProxy(nullptr),
|
||||
m_typesForIpcServer(nullptr),
|
||||
m_typesForIpcServerProxy(nullptr),
|
||||
m_typesForIDataSocket(nullptr),
|
||||
m_typesForIListenSocket(nullptr),
|
||||
m_typesForISocket(nullptr),
|
||||
|
|
|
@ -141,10 +141,6 @@ public:
|
|||
//
|
||||
ClientEvents& forClient();
|
||||
IStreamEvents& forIStream();
|
||||
IpcClientEvents& forIpcClient();
|
||||
IpcClientProxyEvents& forIpcClientProxy();
|
||||
IpcServerEvents& forIpcServer();
|
||||
IpcServerProxyEvents& forIpcServerProxy();
|
||||
IDataSocketEvents& forIDataSocket();
|
||||
IListenSocketEvents& forIListenSocket();
|
||||
ISocketEvents& forISocket();
|
||||
|
@ -163,10 +159,6 @@ public:
|
|||
private:
|
||||
ClientEvents* m_typesForClient;
|
||||
IStreamEvents* m_typesForIStream;
|
||||
IpcClientEvents* m_typesForIpcClient;
|
||||
IpcClientProxyEvents* m_typesForIpcClientProxy;
|
||||
IpcServerEvents* m_typesForIpcServer;
|
||||
IpcServerProxyEvents* m_typesForIpcServerProxy;
|
||||
IDataSocketEvents* m_typesForIDataSocket;
|
||||
IListenSocketEvents* m_typesForIListenSocket;
|
||||
ISocketEvents* m_typesForISocket;
|
||||
|
|
|
@ -57,26 +57,6 @@ REGISTER_EVENT(IStream, outputError)
|
|||
REGISTER_EVENT(IStream, inputShutdown)
|
||||
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
|
||||
//
|
||||
|
@ -178,13 +158,6 @@ REGISTER_EVENT(IScreen, shapeChanged)
|
|||
REGISTER_EVENT(IScreen, suspend)
|
||||
REGISTER_EVENT(IScreen, resume)
|
||||
|
||||
//
|
||||
// IpcServer
|
||||
//
|
||||
|
||||
REGISTER_EVENT(IpcServer, clientConnected)
|
||||
REGISTER_EVENT(IpcServer, messageReceived)
|
||||
|
||||
//
|
||||
// Clipboard
|
||||
//
|
||||
|
|
|
@ -143,89 +143,6 @@ private:
|
|||
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 {
|
||||
public:
|
||||
IDataSocketEvents() :
|
||||
|
|
|
@ -32,10 +32,6 @@ class EventQueueTimer;
|
|||
// Event type registration classes.
|
||||
class ClientEvents;
|
||||
class IStreamEvents;
|
||||
class IpcClientEvents;
|
||||
class IpcClientProxyEvents;
|
||||
class IpcServerEvents;
|
||||
class IpcServerProxyEvents;
|
||||
class IDataSocketEvents;
|
||||
class IListenSocketEvents;
|
||||
class ISocketEvents;
|
||||
|
@ -230,10 +226,6 @@ public:
|
|||
|
||||
virtual ClientEvents& forClient() = 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 IListenSocketEvents& forIListenSocket() = 0;
|
||||
virtual ISocketEvents& forISocket() = 0;
|
||||
|
|
|
@ -29,9 +29,6 @@
|
|||
#include "core/ArgsBase.h"
|
||||
#include "core/XSynergy.h"
|
||||
#include "core/protocol_types.h"
|
||||
#include "ipc/Ipc.h"
|
||||
#include "ipc/IpcMessage.h"
|
||||
#include "ipc/IpcServerProxy.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
|
@ -64,7 +61,6 @@ App::App(IEventQueue* events, ArgsBase* args) :
|
|||
m_args(args),
|
||||
m_fileLog(nullptr),
|
||||
m_appUtil(events),
|
||||
m_ipcClient(nullptr),
|
||||
m_socketMultiplexer(nullptr)
|
||||
{
|
||||
assert(s_instance == nullptr);
|
||||
|
@ -211,35 +207,6 @@ App::initApp(int argc, const char** argv)
|
|||
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
|
||||
App::runEventsLoop(void* /*unused*/)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ipc/IpcClient.h"
|
||||
#include "core/IApp.h"
|
||||
#include "base/String.h"
|
||||
#include "base/Log.h"
|
||||
|
@ -95,12 +94,7 @@ public:
|
|||
|
||||
void setEvents(EventQueue& events) { m_events = &events; }
|
||||
|
||||
private:
|
||||
void handleIpcMessage(const Event&, void*);
|
||||
|
||||
protected:
|
||||
void initIpcClient();
|
||||
void cleanupIpcClient();
|
||||
void runEventsLoop(void*);
|
||||
|
||||
bool m_suspended;
|
||||
|
@ -111,7 +105,6 @@ private:
|
|||
static App* s_instance;
|
||||
FileLogOutputter* m_fileLog;
|
||||
ARCH_APP_UTIL m_appUtil;
|
||||
IpcClient* m_ipcClient;
|
||||
SocketMultiplexer* m_socketMultiplexer;
|
||||
};
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i)
|
|||
argsBase().m_shouldExit = true;
|
||||
}
|
||||
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")) {
|
||||
// supress error when --server is used
|
||||
|
|
|
@ -38,7 +38,6 @@ m_pname(nullptr),
|
|||
m_logFilter(nullptr),
|
||||
m_logFile(nullptr),
|
||||
m_display(nullptr),
|
||||
m_enableIpc(false),
|
||||
m_enableDragDrop(false),
|
||||
m_shouldExit(false),
|
||||
m_profileDirectory(""),
|
||||
|
|
|
@ -35,7 +35,6 @@ public:
|
|||
const char* m_logFile;
|
||||
const char* m_display;
|
||||
String m_name;
|
||||
bool m_enableIpc;
|
||||
bool m_enableDragDrop;
|
||||
#if SYSAPI_WIN32
|
||||
bool m_debugServiceWait;
|
||||
|
|
|
@ -36,5 +36,5 @@ endif()
|
|||
add_library(core STATIC ${sources})
|
||||
|
||||
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()
|
||||
|
|
|
@ -453,12 +453,6 @@ ClientApp::mainLoop()
|
|||
// start client, etc
|
||||
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
|
||||
// later. the timer installed by startClient() will take care of
|
||||
// that.
|
||||
|
@ -489,10 +483,6 @@ ClientApp::mainLoop()
|
|||
updateStatus();
|
||||
LOG((CLOG_NOTE "stopped client"));
|
||||
|
||||
if (argsBase().m_enableIpc) {
|
||||
cleanupIpcClient();
|
||||
}
|
||||
|
||||
return kExitSuccess;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,6 @@
|
|||
#include "core/ArgParser.h"
|
||||
#include "core/ClientArgs.h"
|
||||
#include "core/ServerArgs.h"
|
||||
#include "ipc/IpcClientProxy.h"
|
||||
#include "ipc/IpcLogOutputter.h"
|
||||
#include "ipc/IpcMessage.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
|
@ -44,7 +41,6 @@
|
|||
#include "core/Screen.h"
|
||||
#include "platform/MSWindowsScreen.h"
|
||||
#include "platform/MSWindowsDebugOutputter.h"
|
||||
#include "platform/MSWindowsWatchdog.h"
|
||||
#include "platform/MSWindowsEventQueueBuffer.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
@ -82,8 +78,6 @@ winMainLoopStatic(int, const char**)
|
|||
#endif
|
||||
|
||||
DaemonApp::DaemonApp() :
|
||||
m_ipcServer(nullptr),
|
||||
m_ipcLogOutputter(nullptr),
|
||||
#if SYSAPI_WIN32
|
||||
m_watchdog(nullptr),
|
||||
#endif
|
||||
|
@ -206,53 +200,8 @@ DaemonApp::mainLoop(bool logToFile)
|
|||
// create socket multiplexer. this must happen after daemonization
|
||||
// on unix because threads evaporate across a fork().
|
||||
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();
|
||||
|
||||
#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);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
|
@ -286,117 +235,3 @@ DaemonApp::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
|
||||
|
||||
#include "arch/Arch.h"
|
||||
#include "ipc/IpcServer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class Event;
|
||||
class IpcLogOutputter;
|
||||
class FileLogOutputter;
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
class MSWindowsWatchdog;
|
||||
#endif
|
||||
|
||||
class IEventQueue;
|
||||
|
||||
class DaemonApp {
|
||||
|
||||
public:
|
||||
|
@ -43,7 +42,6 @@ private:
|
|||
void daemonize();
|
||||
void foregroundError(const char* message);
|
||||
std::string logFilename();
|
||||
void handleIpcMessage(const Event&, void*);
|
||||
|
||||
public:
|
||||
static DaemonApp* s_instance;
|
||||
|
@ -53,8 +51,6 @@ public:
|
|||
#endif
|
||||
|
||||
private:
|
||||
IpcServer* m_ipcServer;
|
||||
IpcLogOutputter* m_ipcLogOutputter;
|
||||
IEventQueue* m_events;
|
||||
FileLogOutputter* m_fileLogOutputter;
|
||||
};
|
||||
|
|
|
@ -704,12 +704,6 @@ ServerApp::mainLoop()
|
|||
// start server, etc
|
||||
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
|
||||
ARCH->setSignalHandler(Arch::kHANGUP, &reloadSignalHandler, nullptr);
|
||||
m_events->adoptHandler(m_events->forServerApp().reloadConfig(),
|
||||
|
@ -762,10 +756,6 @@ ServerApp::mainLoop()
|
|||
updateStatus();
|
||||
LOG((CLOG_NOTE "stopped server"));
|
||||
|
||||
if (argsBase().m_enableIpc) {
|
||||
cleanupIpcClient();
|
||||
}
|
||||
|
||||
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})
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(platform io net ipc core client ${libs})
|
||||
target_link_libraries(platform io net core client ${libs})
|
||||
endif()
|
||||
|
||||
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})
|
||||
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(forClient, ClientEvents&());
|
||||
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(forIListenSocket, IListenSocketEvents&());
|
||||
MOCK_METHOD0(forISocket, ISocketEvents&());
|
||||
|
|
|
@ -68,4 +68,4 @@ endif()
|
|||
|
||||
add_executable(unittests ${sources})
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnNonLinux_enableDragDropTrue)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue