2012-06-10 16:50:54 +00:00
|
|
|
/*
|
|
|
|
* synergy -- mouse and keyboard sharing utility
|
2016-09-07 14:24:00 +00:00
|
|
|
* Copyright (C) 2012-2016 Symless Ltd.
|
2012-06-10 16:50:54 +00:00
|
|
|
* 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
|
2015-05-03 02:33:52 +00:00
|
|
|
* found in the file LICENSE that should have accompanied this file.
|
2012-06-10 16:50:54 +00:00
|
|
|
*
|
|
|
|
* 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: split this class into windows and unix to get rid
|
|
|
|
// of all the #ifdefs!
|
|
|
|
|
2014-02-28 12:36:45 +00:00
|
|
|
#include "synergy/DaemonApp.h"
|
2012-06-10 16:50:54 +00:00
|
|
|
|
2014-02-28 12:36:45 +00:00
|
|
|
#include "synergy/App.h"
|
2014-10-23 11:09:09 +00:00
|
|
|
#include "synergy/ArgParser.h"
|
|
|
|
#include "synergy/ServerArgs.h"
|
|
|
|
#include "synergy/ClientArgs.h"
|
2014-02-28 12:36:45 +00:00
|
|
|
#include "ipc/IpcClientProxy.h"
|
|
|
|
#include "ipc/IpcMessage.h"
|
|
|
|
#include "ipc/IpcLogOutputter.h"
|
|
|
|
#include "net/SocketMultiplexer.h"
|
|
|
|
#include "arch/XArch.h"
|
|
|
|
#include "base/Log.h"
|
|
|
|
#include "base/TMethodJob.h"
|
|
|
|
#include "base/TMethodEventJob.h"
|
|
|
|
#include "base/EventQueue.h"
|
|
|
|
#include "base/log_outputters.h"
|
|
|
|
#include "base/Log.h"
|
2012-06-10 16:50:54 +00:00
|
|
|
|
|
|
|
#if SYSAPI_WIN32
|
|
|
|
|
2014-02-28 12:36:45 +00:00
|
|
|
#include "arch/win32/ArchMiscWindows.h"
|
|
|
|
#include "arch/win32/XArchWindows.h"
|
|
|
|
#include "synergy/Screen.h"
|
|
|
|
#include "platform/MSWindowsScreen.h"
|
|
|
|
#include "platform/MSWindowsDebugOutputter.h"
|
|
|
|
#include "platform/MSWindowsWatchdog.h"
|
|
|
|
#include "platform/MSWindowsEventQueueBuffer.h"
|
2012-06-10 16:50:54 +00:00
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <Windows.h>
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2014-02-28 12:36:45 +00:00
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
|
2012-06-10 16:50:54 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp* DaemonApp::s_instance = NULL;
|
2012-06-10 16:50:54 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
mainLoopStatic()
|
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
DaemonApp::s_instance->mainLoop(true);
|
|
|
|
return kExitSuccess;
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
unixMainLoopStatic(int, const char**)
|
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
return mainLoopStatic();
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if SYSAPI_WIN32
|
|
|
|
int
|
|
|
|
winMainLoopStatic(int, const char**)
|
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
return ArchMiscWindows::runDaemon(mainLoopStatic);
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::DaemonApp() :
|
2016-12-28 11:50:32 +00:00
|
|
|
m_ipcServer(nullptr),
|
|
|
|
m_ipcLogOutputter(nullptr),
|
|
|
|
#if SYSAPI_WIN32
|
|
|
|
m_watchdog(nullptr),
|
|
|
|
#endif
|
|
|
|
m_events(nullptr),
|
|
|
|
m_fileLogOutputter(nullptr)
|
2012-06-10 16:50:54 +00:00
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
s_instance = this;
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::~DaemonApp()
|
2012-06-10 16:50:54 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::run(int argc, char** argv)
|
2012-06-10 16:50:54 +00:00
|
|
|
{
|
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
// win32 instance needed for threading, etc.
|
|
|
|
ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
|
|
|
|
Arch arch;
|
|
|
|
arch.init();
|
2012-07-10 01:51:51 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
Log log;
|
|
|
|
EventQueue events;
|
|
|
|
m_events = &events;
|
2012-06-10 16:50:54 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
bool uninstall = false;
|
|
|
|
try
|
|
|
|
{
|
2012-06-10 16:50:54 +00:00
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
// sends debug messages to visual studio console window.
|
|
|
|
log.insert(new MSWindowsDebugOutputter());
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
// default log level to system setting.
|
|
|
|
string logLevel = arch.setting("LogLevel");
|
|
|
|
if (logLevel != "")
|
|
|
|
log.setFilter(logLevel.c_str());
|
2012-06-10 16:50:54 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
bool foreground = false;
|
2012-06-10 16:50:54 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
string arg(argv[i]);
|
2012-06-10 16:50:54 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
if (arg == "/f" || arg == "-f") {
|
|
|
|
foreground = true;
|
|
|
|
}
|
2012-06-10 16:50:54 +00:00
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
else if (arg == "/install") {
|
|
|
|
uninstall = true;
|
|
|
|
arch.installDaemon();
|
|
|
|
return kExitSuccess;
|
|
|
|
}
|
|
|
|
else if (arg == "/uninstall") {
|
|
|
|
arch.uninstallDaemon();
|
|
|
|
return kExitSuccess;
|
|
|
|
}
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
else {
|
|
|
|
stringstream ss;
|
|
|
|
ss << "Unrecognized argument: " << arg;
|
|
|
|
foregroundError(ss.str().c_str());
|
|
|
|
return kExitArgs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (foreground) {
|
|
|
|
// run process in foreground instead of daemonizing.
|
|
|
|
// useful for debugging.
|
|
|
|
mainLoop(false);
|
|
|
|
}
|
|
|
|
else {
|
2012-06-10 16:50:54 +00:00
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
arch.daemonize("Synergy", winMainLoopStatic);
|
2012-06-10 16:50:54 +00:00
|
|
|
#elif SYSAPI_UNIX
|
2016-12-28 11:50:32 +00:00
|
|
|
arch.daemonize("Synergy", unixMainLoopStatic);
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return kExitSuccess;
|
|
|
|
}
|
|
|
|
catch (XArch& e) {
|
|
|
|
String message = e.what();
|
|
|
|
if (uninstall && (message.find("The service has not been started") != String::npos)) {
|
|
|
|
// TODO: if we're keeping this use error code instead (what is it?!).
|
|
|
|
// HACK: this message happens intermittently, not sure where from but
|
|
|
|
// it's quite misleading for the user. they thing something has gone
|
|
|
|
// horribly wrong, but it's just the service manager reporting a false
|
|
|
|
// positive (the service has actually shut down in most cases).
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
foregroundError(message.c_str());
|
|
|
|
}
|
|
|
|
return kExitFailed;
|
|
|
|
}
|
|
|
|
catch (std::exception& e) {
|
|
|
|
foregroundError(e.what());
|
|
|
|
return kExitFailed;
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
foregroundError("Unrecognized error.");
|
|
|
|
return kExitFailed;
|
|
|
|
}
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::mainLoop(bool logToFile)
|
2012-06-10 16:50:54 +00:00
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
DAEMON_RUNNING(true);
|
|
|
|
|
|
|
|
if (logToFile) {
|
|
|
|
m_fileLogOutputter = new FileLogOutputter(logFilename().c_str());
|
|
|
|
CLOG->insert(m_fileLogOutputter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2012-07-05 18:05:35 +00:00
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
m_watchdog = new MSWindowsWatchdog(false, *m_ipcServer, *m_ipcLogOutputter);
|
|
|
|
m_watchdog->setFileLogOutputter(m_fileLogOutputter);
|
2012-07-05 18:05:35 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
|
|
|
|
m_events->adoptHandler(
|
|
|
|
m_events->forIpcServer().messageReceived(), m_ipcServer,
|
|
|
|
new TMethodEventJob<DaemonApp>(this, &DaemonApp::handleIpcMessage));
|
2012-07-03 14:15:05 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
m_ipcServer->listen();
|
|
|
|
|
2012-06-10 16:50:54 +00:00
|
|
|
#if SYSAPI_WIN32
|
2013-10-15 15:46:02 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
// 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();
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
m_events->loop();
|
2012-06-10 16:50:54 +00:00
|
|
|
|
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
m_watchdog->stop();
|
|
|
|
delete m_watchdog;
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
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) {
|
|
|
|
LOG((CLOG_CRIT "An error occurred: %s", e.what()));
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
LOG((CLOG_CRIT "An unknown error occurred.\n"));
|
|
|
|
}
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::foregroundError(const char* message)
|
2012-06-10 16:50:54 +00:00
|
|
|
{
|
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
MessageBox(NULL, message, "Synergy Service", MB_OK | MB_ICONERROR);
|
2012-06-10 16:50:54 +00:00
|
|
|
#elif SYSAPI_UNIX
|
2016-12-28 11:50:32 +00:00
|
|
|
cerr << message << endl;
|
2012-06-10 16:50:54 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::logFilename()
|
2012-06-10 16:50:54 +00:00
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
string logFilename;
|
|
|
|
logFilename = ARCH->setting("LogFilename");
|
|
|
|
if (logFilename.empty()) {
|
|
|
|
logFilename = ARCH->getLogDirectory();
|
|
|
|
logFilename.append("/");
|
|
|
|
logFilename.append(LOG_FILENAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
return logFilename;
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|
|
|
|
|
2012-07-03 14:15:05 +00:00
|
|
|
void
|
2014-11-11 13:51:47 +00:00
|
|
|
DaemonApp::handleIpcMessage(const Event& e, void*)
|
2012-07-03 14:15:05 +00:00
|
|
|
{
|
2016-12-28 11:50:32 +00:00
|
|
|
IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
|
|
|
|
switch (m->type()) {
|
|
|
|
case kIpcCommand: {
|
|
|
|
IpcCommandMessage* cm = static_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(NULL);
|
|
|
|
const char** argv = argParser.getArgv(argsArray);
|
|
|
|
ServerArgs serverArgs;
|
|
|
|
ClientArgs clientArgs;
|
|
|
|
int argc = static_cast<int>(argsArray.size());
|
|
|
|
bool server = argsArray[0].find("synergys") != String::npos ? true : false;
|
|
|
|
ArgsBase* argBase = NULL;
|
|
|
|
|
|
|
|
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()));
|
|
|
|
}
|
|
|
|
}
|
2014-10-27 14:11:43 +00:00
|
|
|
|
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
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());
|
2014-10-27 14:11:43 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
|
|
|
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: 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()));
|
|
|
|
}
|
2012-07-10 18:35:33 +00:00
|
|
|
|
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
// 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());
|
2012-07-10 18:35:33 +00:00
|
|
|
#endif
|
2016-12-28 11:50:32 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-07-09 12:09:24 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
case kIpcHello:
|
|
|
|
IpcHelloMessage* hm = static_cast<IpcHelloMessage*>(m);
|
|
|
|
String type;
|
|
|
|
switch (hm->clientType()) {
|
|
|
|
case kIpcClientGui: type = "gui"; break;
|
|
|
|
case kIpcClientNode: type = "node"; break;
|
|
|
|
default: type = "unknown"; break;
|
|
|
|
}
|
2013-10-16 15:30:42 +00:00
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
LOG((CLOG_DEBUG "ipc hello, type=%s", type.c_str()));
|
2013-10-16 15:30:42 +00:00
|
|
|
|
|
|
|
#if SYSAPI_WIN32
|
2016-12-28 11:50:32 +00:00
|
|
|
String watchdogStatus = m_watchdog->isProcessActive() ? "ok" : "error";
|
|
|
|
LOG((CLOG_INFO "watchdog status: %s", watchdogStatus.c_str()));
|
2013-10-16 15:30:42 +00:00
|
|
|
#endif
|
|
|
|
|
2016-12-28 11:50:32 +00:00
|
|
|
m_ipcLogOutputter->notifyBuffer();
|
|
|
|
break;
|
|
|
|
}
|
2012-06-10 16:50:54 +00:00
|
|
|
}
|