/* * synergy-plus -- mouse and keyboard sharing utility * Copyright (C) 2009 The Synergy+ Project * 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 COPYING 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 . */ #include "CArchAppUtilWindows.h" #include "Version.h" #include "CLog.h" #include "XArchWindows.h" #include "CArchMiscWindows.h" #include "CApp.h" #include "LogOutputters.h" #include "CMSWindowsScreen.h" #include "XSynergy.h" #include "IArchTaskBarReceiver.h" #include "CMSWindowsRelauncher.h" #include "CScreen.h" #include #include #include CArchAppUtilWindows::CArchAppUtilWindows() : m_exitMode(kExitModeNormal) { if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE) { throw XArchEvalWindows(); } } CArchAppUtilWindows::~CArchAppUtilWindows() { } BOOL WINAPI CArchAppUtilWindows::consoleHandler(DWORD CEvent) { if (instance().app().m_taskBarReceiver) { // HACK: it would be nice to delete the s_taskBarReceiver object, but // this is best done by the CApp destructor; however i don't feel like // opening up that can of worms today... i need sleep. instance().app().m_taskBarReceiver->cleanup(); } ExitProcess(kExitTerminated); return TRUE; } bool CArchAppUtilWindows::parseArg(const int& argc, const char* const* argv, int& i) { if (app().isArg(i, argc, argv, NULL, "--service")) { const char* action = argv[++i]; if (_stricmp(action, "install") == 0) { installService(); } else if (_stricmp(action, "uninstall") == 0) { uninstallService(); } else if (_stricmp(action, "start") == 0) { startService(); } else if (_stricmp(action, "stop") == 0) { stopService(); } else { LOG((CLOG_ERR "unknown service action: %s", action)); app().m_bye(kExitArgs); } app().m_bye(kExitSuccess); } else if (app().isArg(i, argc, argv, NULL, "--debug-service-wait")) { app().argsBase().m_debugServiceWait = true; } else if (app().isArg(i, argc, argv, NULL, "--relaunch")) { app().argsBase().m_relaunchMode = true; } else if (app().isArg(i, argc, argv, NULL, "--exit-pause")) { app().argsBase().m_pauseOnExit = true; } else if (app().isArg(i, argc, argv, NULL, "--no-tray")) { app().argsBase().m_disableTray = true; } else { // option not supported here return false; } return true; } CString CArchAppUtilWindows::getServiceArgs() const { std::stringstream argBuf; for (int i = 1; i < __argc; i++) { const char* arg = __argv[i]; // ignore service setup args if (_stricmp(arg, "--service") == 0) { // ignore and skip the next arg also (service action) i++; } else { if (strchr(arg, ' ') != NULL) { // surround argument with quotes if it contains a space argBuf << " \"" << arg << "\""; } else { argBuf << " " << arg; } } } return argBuf.str(); } void CArchAppUtilWindows::installService() { CString args = getServiceArgs(); // get the path of this program char thisPath[MAX_PATH]; GetModuleFileName(CArchMiscWindows::instanceWin32(), thisPath, MAX_PATH); ARCH->installDaemon( app().daemonName(), app().daemonInfo(), thisPath, args.c_str(), NULL, true); LOG((CLOG_INFO "service '%s' installed with args: %s", app().daemonName(), args != "" ? args.c_str() : "none" )); } void CArchAppUtilWindows::uninstallService() { ARCH->uninstallDaemon(app().daemonName(), true); LOG((CLOG_INFO "service '%s' uninstalled", app().daemonName())); } void CArchAppUtilWindows::startService() { // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); if (mgr == NULL) { throw XArchDaemonFailed(new XArchEvalWindows()); } // open the service SC_HANDLE service = OpenService( mgr, app().daemonName(), SERVICE_START); if (service == NULL) { CloseServiceHandle(mgr); throw XArchDaemonFailed(new XArchEvalWindows()); } // start the service if (StartService(service, 0, NULL)) { LOG((CLOG_INFO "service '%s' started", app().daemonName())); } else { throw XArchDaemonFailed(new XArchEvalWindows()); } } void CArchAppUtilWindows::stopService() { // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); if (mgr == NULL) { throw XArchDaemonFailed(new XArchEvalWindows()); } // open the service SC_HANDLE service = OpenService( mgr, app().daemonName(), SERVICE_STOP | SERVICE_QUERY_STATUS); if (service == NULL) { CloseServiceHandle(mgr); throw XArchDaemonFailed(new XArchEvalWindows()); } // ask the service to stop, asynchronously SERVICE_STATUS ss; if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) { DWORD dwErrCode = GetLastError(); if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) { LOG((CLOG_ERR "cannot stop service '%s'", app().daemonName())); throw XArchDaemonFailed(new XArchEvalWindows()); } } LOG((CLOG_INFO "service '%s' stopping asynchronously", app().daemonName())); } static int mainLoopStatic() { return CArchAppUtil::instance().app().mainLoop(); } int CArchAppUtilWindows::daemonNTMainLoop(int argc, const char** argv) { app().initApp(argc, argv); debugServiceWait(); // NB: what the hell does this do?! app().argsBase().m_backend = false; return CArchMiscWindows::runDaemon(mainLoopStatic); } void CArchAppUtilWindows::exitApp(int code) { switch (m_exitMode) { case kExitModeDaemon: CArchMiscWindows::daemonFailed(code); break; default: throw XExitApp(code); } } int daemonNTMainLoopStatic(int argc, const char** argv) { return CArchAppUtilWindows::instance().daemonNTMainLoop(argc, argv); } int CArchAppUtilWindows::daemonNTStartup(int, char**) { CSystemLogger sysLogger(app().daemonName(), false); m_exitMode = kExitModeDaemon; return ARCH->daemonize(app().daemonName(), daemonNTMainLoopStatic); } static int daemonNTStartupStatic(int argc, char** argv) { return CArchAppUtilWindows::instance().daemonNTStartup(argc, argv); } static int foregroundStartupStatic(int argc, char** argv) { return CArchAppUtil::instance().app().foregroundStartup(argc, argv); } void CArchAppUtilWindows::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 CArchAppUtilWindows::run(int argc, char** argv) { // record window instance for tray icon, etc CArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); CMSWindowsScreen::init(CArchMiscWindows::instanceWin32()); CThread::getCurrentThread().setPriority(-14); StartupFunc startup; if (CArchMiscWindows::wasLaunchedAsService()) { startup = &daemonNTStartupStatic; } else { startup = &foregroundStartupStatic; app().argsBase().m_daemon = false; } return app().runInner(argc, argv, NULL, startup); } CArchAppUtilWindows& CArchAppUtilWindows::instance() { return (CArchAppUtilWindows&)CArchAppUtil::instance(); } void CArchAppUtilWindows::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 CArchAppUtilWindows::startNode() { if (app().argsBase().m_relaunchMode) { LOG((CLOG_DEBUG1 "entering relaunch mode")); CMSWindowsRelauncher relauncher; relauncher.startAsync(); // HACK: create a dummy screen, which can handle system events // (such as a stop request from the service controller). CScreen* dummyScreen = app().createScreen(); } else { app().startNode(); } }