summaryrefslogtreecommitdiffstats
path: root/src/lib/barrier/win32
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/barrier/win32')
-rw-r--r--src/lib/barrier/win32/AppUtilWindows.cpp185
-rw-r--r--src/lib/barrier/win32/AppUtilWindows.h62
-rw-r--r--src/lib/barrier/win32/DaemonApp.cpp361
-rw-r--r--src/lib/barrier/win32/DaemonApp.h58
4 files changed, 666 insertions, 0 deletions
diff --git a/src/lib/barrier/win32/AppUtilWindows.cpp b/src/lib/barrier/win32/AppUtilWindows.cpp
new file mode 100644
index 0000000..560b029
--- /dev/null
+++ b/src/lib/barrier/win32/AppUtilWindows.cpp
@@ -0,0 +1,185 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 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 "barrier/win32/AppUtilWindows.h"
+#include "barrier/Screen.h"
+#include "barrier/ArgsBase.h"
+#include "barrier/App.h"
+#include "barrier/XBarrier.h"
+#include "platform/MSWindowsScreen.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/Log.h"
+#include "base/log_outputters.h"
+#include "base/IEventQueue.h"
+#include "base/Event.h"
+#include "base/EventQueue.h"
+#include "common/Version.h"
+
+#include <sstream>
+#include <iostream>
+#include <conio.h>
+#include <VersionHelpers.h>
+
+AppUtilWindows::AppUtilWindows(IEventQueue* events) :
+ m_events(events),
+ m_exitMode(kExitModeNormal)
+{
+ if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE)
+ {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+AppUtilWindows::~AppUtilWindows()
+{
+}
+
+BOOL WINAPI AppUtilWindows::consoleHandler(DWORD)
+{
+ LOG((CLOG_INFO "got shutdown signal"));
+ IEventQueue* events = AppUtil::instance().app().getEvents();
+ events->addEvent(Event(Event::kQuit));
+ return TRUE;
+}
+
+static
+int
+mainLoopStatic()
+{
+ return AppUtil::instance().app().mainLoop();
+}
+
+int
+AppUtilWindows::daemonNTMainLoop(int argc, const char** argv)
+{
+ app().initApp(argc, argv);
+ debugServiceWait();
+
+ // NB: what the hell does this do?!
+ app().argsBase().m_backend = false;
+
+ return ArchMiscWindows::runDaemon(mainLoopStatic);
+}
+
+void
+AppUtilWindows::exitApp(int code)
+{
+ switch (m_exitMode) {
+
+ case kExitModeDaemon:
+ ArchMiscWindows::daemonFailed(code);
+ break;
+
+ default:
+ throw XExitApp(code);
+ }
+}
+
+int daemonNTMainLoopStatic(int argc, const char** argv)
+{
+ return AppUtilWindows::instance().daemonNTMainLoop(argc, argv);
+}
+
+int
+AppUtilWindows::daemonNTStartup(int, char**)
+{
+ SystemLogger sysLogger(app().daemonName(), false);
+ m_exitMode = kExitModeDaemon;
+ return ARCH->daemonize(app().daemonName(), daemonNTMainLoopStatic);
+}
+
+static
+int
+daemonNTStartupStatic(int argc, char** argv)
+{
+ return AppUtilWindows::instance().daemonNTStartup(argc, argv);
+}
+
+static
+int
+foregroundStartupStatic(int argc, char** argv)
+{
+ return AppUtil::instance().app().foregroundStartup(argc, argv);
+}
+
+void
+AppUtilWindows::beforeAppExit()
+{
+ // this can be handy for debugging, since the application is launched in
+ // a new console window, and will normally close on exit (making it so
+ // that we can't see error messages).
+ if (app().argsBase().m_pauseOnExit) {
+ std::cout << std::endl << "press any key to exit..." << std::endl;
+ int c = _getch();
+ }
+}
+
+int
+AppUtilWindows::run(int argc, char** argv)
+{
+ if (!IsWindowsXPSP3OrGreater()) {
+ throw std::runtime_error("Barrier only supports Windows XP SP3 and above.");
+ }
+
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+
+ MSWindowsScreen::init(ArchMiscWindows::instanceWin32());
+ Thread::getCurrentThread().setPriority(-14);
+
+ StartupFunc startup;
+ if (ArchMiscWindows::wasLaunchedAsService()) {
+ startup = &daemonNTStartupStatic;
+ } else {
+ startup = &foregroundStartupStatic;
+ app().argsBase().m_daemon = false;
+ }
+
+ return app().runInner(argc, argv, NULL, startup);
+}
+
+AppUtilWindows&
+AppUtilWindows::instance()
+{
+ return (AppUtilWindows&)AppUtil::instance();
+}
+
+void
+AppUtilWindows::debugServiceWait()
+{
+ if (app().argsBase().m_debugServiceWait)
+ {
+ while(true)
+ {
+ // this code is only executed when the process is launched via the
+ // windows service controller (and --debug-service-wait arg is
+ // used). to debug, set a breakpoint on this line so that
+ // execution is delayed until the debugger is attached.
+ ARCH->sleep(1);
+ LOG((CLOG_INFO "waiting for debugger to attach"));
+ }
+ }
+}
+
+void
+AppUtilWindows::startNode()
+{
+ app().startNode();
+}
diff --git a/src/lib/barrier/win32/AppUtilWindows.h b/src/lib/barrier/win32/AppUtilWindows.h
new file mode 100644
index 0000000..c5da228
--- /dev/null
+++ b/src/lib/barrier/win32/AppUtilWindows.h
@@ -0,0 +1,62 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 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 "barrier/AppUtil.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include "Windows.h"
+
+#define ARCH_APP_UTIL AppUtilWindows
+
+class IEventQueue;
+
+enum AppExitMode {
+ kExitModeNormal,
+ kExitModeDaemon
+};
+
+class AppUtilWindows : public AppUtil {
+public:
+ AppUtilWindows(IEventQueue* events);
+ virtual ~AppUtilWindows();
+
+ int daemonNTStartup(int, char**);
+
+ int daemonNTMainLoop(int argc, const char** argv);
+
+ void debugServiceWait();
+
+ int run(int argc, char** argv);
+
+ void exitApp(int code);
+
+ void beforeAppExit();
+
+ static AppUtilWindows& instance();
+
+ void startNode();
+
+private:
+ AppExitMode m_exitMode;
+ IEventQueue* m_events;
+
+ static BOOL WINAPI consoleHandler(DWORD Event);
+};
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;
+ }
+}
diff --git a/src/lib/barrier/win32/DaemonApp.h b/src/lib/barrier/win32/DaemonApp.h
new file mode 100644
index 0000000..2a8484b
--- /dev/null
+++ b/src/lib/barrier/win32/DaemonApp.h
@@ -0,0 +1,58 @@
+/*
+ * 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/>.
+ */
+
+#pragma once
+
+#include "arch/Arch.h"
+#include "ipc/IpcServer.h"
+
+#include <string>
+
+class Event;
+class IpcLogOutputter;
+class FileLogOutputter;
+
+class MSWindowsWatchdog;
+
+class DaemonApp {
+
+public:
+ DaemonApp();
+ virtual ~DaemonApp();
+ int run(int argc, char** argv);
+ void mainLoop(bool daemonized);
+
+private:
+ void daemonize();
+ void foregroundError(const char* message);
+ std::string logFilename();
+ void handleIpcMessage(const Event&, void*);
+
+public:
+ static DaemonApp* s_instance;
+
+ MSWindowsWatchdog* m_watchdog;
+
+private:
+ IpcServer* m_ipcServer;
+ IpcLogOutputter* m_ipcLogOutputter;
+ IEventQueue* m_events;
+ FileLogOutputter* m_fileLogOutputter;
+};
+
+#define LOG_FILENAME "barrierd.log"