diff options
| author | 2018-04-25 18:07:30 -0400 | |
|---|---|---|
| committer | 2018-04-25 18:07:30 -0400 | |
| commit | 9b1b081cfdb1c0fb6457278775e0823f8bc10f62 (patch) | |
| tree | ce8840148d8445055ba9e4f12263b2208f234c16 /src/lib/barrier/win32/DaemonApp.cpp | |
Import Upstream version 2.0.0+dfsgupstream/2.0.0+dfsg
Diffstat (limited to 'src/lib/barrier/win32/DaemonApp.cpp')
| -rw-r--r-- | src/lib/barrier/win32/DaemonApp.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/lib/barrier/win32/DaemonApp.cpp b/src/lib/barrier/win32/DaemonApp.cpp new file mode 100644 index 0000000..62fecf8 --- /dev/null +++ b/src/lib/barrier/win32/DaemonApp.cpp @@ -0,0 +1,361 @@ +/* + * barrier -- 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 "barrier/win32/DaemonApp.h" + +#include "barrier/App.h" +#include "barrier/ArgParser.h" +#include "barrier/ServerArgs.h" +#include "barrier/ClientArgs.h" +#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" + +#include "arch/win32/ArchMiscWindows.h" +#include "arch/win32/XArchWindows.h" +#include "barrier/Screen.h" +#include "platform/MSWindowsScreen.h" +#include "platform/MSWindowsDebugOutputter.h" +#include "platform/MSWindowsWatchdog.h" +#include "platform/MSWindowsEventQueueBuffer.h" + +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> + +#include <string> +#include <iostream> +#include <sstream> + +using namespace std; + +DaemonApp* DaemonApp::s_instance = NULL; + +int +mainLoopStatic() +{ + DaemonApp::s_instance->mainLoop(true); + return kExitSuccess; +} + +int +mainLoopStatic(int, const char**) +{ + return ArchMiscWindows::runDaemon(mainLoopStatic); +} + +DaemonApp::DaemonApp() : + m_ipcServer(nullptr), + m_ipcLogOutputter(nullptr), + m_watchdog(nullptr), + m_events(nullptr), + m_fileLogOutputter(nullptr) +{ + s_instance = this; +} + +DaemonApp::~DaemonApp() +{ +} + +int +DaemonApp::run(int argc, char** argv) +{ + // win32 instance needed for threading, etc. + ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); + + Arch arch; + arch.init(); + + Log log; + EventQueue events; + m_events = &events; + + bool uninstall = false; + try + { + // sends debug messages to visual studio console window. + log.insert(new MSWindowsDebugOutputter()); + + // default log level to system setting. + string logLevel = arch.setting("LogLevel"); + if (logLevel != "") + log.setFilter(logLevel.c_str()); + + bool foreground = false; + + for (int i = 1; i < argc; ++i) { + string arg(argv[i]); + + if (arg == "/f" || arg == "-f") { + foreground = true; + } + else if (arg == "/install") { + uninstall = true; + arch.installDaemon(); + return kExitSuccess; + } + else if (arg == "/uninstall") { + arch.uninstallDaemon(); + return kExitSuccess; + } + 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 { + arch.daemonize("Barrier", mainLoopStatic); + } + + 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; + } +} + +void +DaemonApp::mainLoop(bool daemonized) +{ + try + { + DAEMON_RUNNING(true); + + if (daemonized) { + 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); + + m_watchdog = new MSWindowsWatchdog(daemonized, false, *m_ipcServer, *m_ipcLogOutputter); + m_watchdog->setFileLogOutputter(m_fileLogOutputter); + + m_events->adoptHandler( + m_events->forIpcServer().messageReceived(), m_ipcServer, + new TMethodEventJob<DaemonApp>(this, &DaemonApp::handleIpcMessage)); + + m_ipcServer->listen(); + + // 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(); + + m_events->loop(); + + m_watchdog->stop(); + delete m_watchdog; + + 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")); + } +} + +void +DaemonApp::foregroundError(const char* message) +{ + MessageBox(NULL, message, "Barrier Service", MB_OK | MB_ICONERROR); +} + +std::string +DaemonApp::logFilename() +{ + string logFilename; + logFilename = ARCH->setting("LogFilename"); + if (logFilename.empty()) { + logFilename = ARCH->getLogDirectory(); + logFilename.append("/"); + logFilename.append(LOG_FILENAME); + } + + return logFilename; +} + +void +DaemonApp::handleIpcMessage(const Event& e, void*) +{ + 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("barriers") != 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())); + } + } + + // eg. no log-to-file while running in foreground + if (m_fileLogOutputter != nullptr) { + 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()); + } + } + 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())); + } + + // 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()); + + break; + } + + 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; + } + + LOG((CLOG_DEBUG "ipc hello, type=%s", type.c_str())); + + const char * serverstatus = m_watchdog->isProcessActive() ? "active" : "not active"; + LOG((CLOG_INFO "server status: %s", serverstatus)); + + m_ipcLogOutputter->notifyBuffer(); + break; + } +} |
