diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 6e3f3b49..f3509b83 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -49,7 +49,8 @@ const char g_activeDesktop[] = {"activeDesktop:"}; MSWindowsWatchdog::MSWindowsWatchdog( bool autoDetectCommand, IpcServer& ipcServer, - IpcLogOutputter& ipcLogOutputter) : + IpcLogOutputter& ipcLogOutputter, + bool foreground) : m_thread(NULL), m_autoDetectCommand(autoDetectCommand), m_monitoring(true), @@ -63,7 +64,8 @@ MSWindowsWatchdog::MSWindowsWatchdog( m_processRunning(false), m_fileLogOutputter(NULL), m_autoElevated(false), - m_ready(false) + m_ready(false), + m_foreground(foreground) { m_mutex = ARCH->newMutex(); m_condVar = ARCH->newCondVar(); @@ -105,104 +107,118 @@ MSWindowsWatchdog::stop() HANDLE MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security) { - HANDLE sourceToken; + HANDLE sourceToken; - BOOL tokenRet = OpenProcessToken( - process, - TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, - &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)); + if (!tokenRet) { + LOG((CLOG_ERR "could not open token, process handle: %d", process)); + throw XArch(new XArchEvalWindows()); + } - HANDLE newToken; - BOOL duplicateRet = DuplicateTokenEx( - sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security, - SecurityImpersonation, TokenPrimary, &newToken); + LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken)); - 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 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 +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"); - } + // 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)) { - return duplicateProcessToken(process, security); - } - else { - LOG((CLOG_DEBUG "getting non-elevated token")); - return m_session.getUserToken(security); - } + 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(); + shutdownExistingProcesses(); - SendSas sendSasFunc = NULL; - HINSTANCE sasLib = LoadLibrary("sas.dll"); - if (sasLib) { - LOG((CLOG_DEBUG "found sas.dll")); - sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS"); - } + 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; + 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()); - } + if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) { + throw XArch(new XArchEvalWindows()); + } - ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION)); - while (m_monitoring) { - try { + 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_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_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()) { + bool startNeeded = false; + + if (m_processFailures != 0) { + startNeeded = true; + } + else if (!m_foreground && m_session.hasChanged()) { + startNeeded = true; + } + else if (m_commandChanged) { + startNeeded = true; + } + + if (startNeeded) { + startProcess(); + } + } if (m_processRunning && !isProcessActive()) { @@ -284,28 +300,35 @@ MSWindowsWatchdog::startProcess() m_processRunning = false; } - m_session.updateActiveSession(); + BOOL createRet; + if (m_foreground) { + LOG((CLOG_DEBUG "starting command in foreground")); + createRet = startProcessInForeground(m_command); + } + else { + LOG((CLOG_DEBUG "starting command as session user")); + m_session.updateActiveSession(); - SECURITY_ATTRIBUTES sa; - ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - getActiveDesktop(&sa); + getActiveDesktop(&sa); - ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - HANDLE userToken = getUserToken(&sa); - m_elevateProcess = m_autoElevated ? m_autoElevated : m_elevateProcess; - m_autoElevated = false; + 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)); + // patch by Jack Zhou and Henry Tung + // set UIAccess to fix Windows 8 GUI interaction + DWORD uiAccess = 1; + SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD)); - BOOL createRet = doStartProcess(m_command, userToken, &sa); + createRet = startProcessAsUser(m_command, userToken, &sa); + } if (!createRet) { - LOG((CLOG_ERR "could not launch")); + LOG((CLOG_ERR "could not launch command")); DWORD exitCode = 0; GetExitCodeProcess(m_processInfo.hProcess, &exitCode); LOG((CLOG_ERR "exit code: %d", exitCode)); @@ -329,7 +352,21 @@ MSWindowsWatchdog::startProcess() } BOOL -MSWindowsWatchdog::doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa) +MSWindowsWatchdog::startProcessInForeground(String& command) +{ + // clear, as we're reusing process info struct + ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION)); + + STARTUPINFO startupInfo; + ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); + + return CreateProcess( + NULL, LPSTR(command.c_str()), NULL, NULL, + FALSE, 0, NULL, NULL, &startupInfo, &m_processInfo); +} + +BOOL +MSWindowsWatchdog::startProcessAsUser(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa) { // clear, as we're reusing process info struct ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION)); @@ -542,7 +579,7 @@ MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) HANDLE userToken = getUserToken(security); m_elevateProcess = elevateProcess; - BOOL createRet = doStartProcess(syntoolCommand, userToken, security); + BOOL createRet = startProcessAsUser(syntoolCommand, userToken, security); if (!createRet) { DWORD rc = GetLastError(); diff --git a/src/lib/platform/MSWindowsWatchdog.h b/src/lib/platform/MSWindowsWatchdog.h index 76546539..692af3d5 100644 --- a/src/lib/platform/MSWindowsWatchdog.h +++ b/src/lib/platform/MSWindowsWatchdog.h @@ -37,7 +37,8 @@ public: MSWindowsWatchdog( bool autoDetectCommand, IpcServer& ipcServer, - IpcLogOutputter& ipcLogOutputter); + IpcLogOutputter& ipcLogOutputter, + bool foreground); virtual ~MSWindowsWatchdog(); void startAsync(); @@ -55,7 +56,8 @@ private: HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security); HANDLE getUserToken(LPSECURITY_ATTRIBUTES security); void startProcess(); - BOOL doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa); + BOOL startProcessAsUser(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa); + BOOL startProcessInForeground(String& command); void sendSas(); void getActiveDesktop(LPSECURITY_ATTRIBUTES security); void testOutput(String buffer); @@ -81,6 +83,7 @@ private: ArchMutex m_mutex; ArchCond m_condVar; bool m_ready; + bool m_foreground; }; //! Relauncher error diff --git a/src/lib/synergy/DaemonApp.cpp b/src/lib/synergy/DaemonApp.cpp index 58b8c499..65582c46 100644 --- a/src/lib/synergy/DaemonApp.cpp +++ b/src/lib/synergy/DaemonApp.cpp @@ -155,7 +155,7 @@ DaemonApp::run(int argc, char** argv) if (foreground) { // run process in foreground instead of daemonizing. // useful for debugging. - mainLoop(false); + mainLoop(false, foreground); } else { #if SYSAPI_WIN32 @@ -192,7 +192,7 @@ DaemonApp::run(int argc, char** argv) } void -DaemonApp::mainLoop(bool logToFile) +DaemonApp::mainLoop(bool logToFile, bool foreground) { try { @@ -215,7 +215,7 @@ DaemonApp::mainLoop(bool logToFile) CLOG->insert(m_ipcLogOutputter); #if SYSAPI_WIN32 - m_watchdog = new MSWindowsWatchdog(false, *m_ipcServer, *m_ipcLogOutputter); + m_watchdog = new MSWindowsWatchdog(false, *m_ipcServer, *m_ipcLogOutputter, foreground); m_watchdog->setFileLogOutputter(m_fileLogOutputter); #endif diff --git a/src/lib/synergy/DaemonApp.h b/src/lib/synergy/DaemonApp.h index 511c6d53..080c2b99 100644 --- a/src/lib/synergy/DaemonApp.h +++ b/src/lib/synergy/DaemonApp.h @@ -37,7 +37,7 @@ public: DaemonApp(); virtual ~DaemonApp(); int run(int argc, char** argv); - void mainLoop(bool logToFile); + void mainLoop(bool logToFile, bool foreground = false); private: void daemonize();