HANDLE
MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security)
{
	HANDLE 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));

	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;
}
String
WinINetRequest::send()
{
    if (m_used) {
        throw XArch("class is one time use.");
    }
    m_used = true;

    openSession();
    connect();
    openRequest();
    
    String headers("Content-Type: text/html");
    if (!HttpSendRequest(m_request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) {
        throw XArch(new XArchEvalWindows());
    }
    
    std::stringstream result;
    CHAR buffer[1025];
    DWORD read = 0;

    while (InternetReadFile(m_request, buffer, sizeof(buffer) - 1, &read) && (read != 0)) {
        buffer[read] = 0;
        result << buffer;
        read = 0;
    }

    return result.str();
}
void
MSWindowsWatchdog::shutdownExistingProcesses()
{
	// first we need to take a snapshot of the running processes
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (snapshot == INVALID_HANDLE_VALUE) {
		LOG((CLOG_ERR "could not get process snapshot"));
		throw XArch(new XArchEvalWindows);
	}

	PROCESSENTRY32 entry;
	entry.dwSize = sizeof(PROCESSENTRY32);

	// get the first process, and if we can't do that then it's 
	// unlikely we can go any further
	BOOL gotEntry = Process32First(snapshot, &entry);
	if (!gotEntry) {
		LOG((CLOG_ERR "could not get first process entry"));
		throw XArch(new XArchEvalWindows);
	}

	// now just iterate until we can find winlogon.exe pid
	DWORD pid = 0;
	while (gotEntry) {

		// make sure we're not checking the system process
		if (entry.th32ProcessID != 0) {

			if (_stricmp(entry.szExeFile, "synergyc.exe") == 0 ||
				_stricmp(entry.szExeFile, "synergys.exe") == 0) {
				
				HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
				shutdownProcess(handle, entry.th32ProcessID, 10);
			}
		}

		// now move on to the next entry (if we're not at the end)
		gotEntry = Process32Next(snapshot, &entry);
		if (!gotEntry) {

			DWORD err = GetLastError();
			if (err != ERROR_NO_MORE_FILES) {

				// only worry about error if it's not the end of the snapshot
				LOG((CLOG_ERR "could not get subsiquent process entry"));
				throw XArch(new XArchEvalWindows);
			}
		}
	}

	CloseHandle(snapshot);
	m_processRunning = false;
}
Exemple #4
0
void
ArchPluginWindows::load()
{
	String dir = getPluginsDir();
	LOG((CLOG_DEBUG "plugins dir: %s", dir.c_str()));

	String pattern = String(dir).append("\\*.dll");
	std::vector<String> plugins;
	getFilenames(pattern, plugins);

	std::vector<String>::iterator it;
	for (it = plugins.begin(); it != plugins.end(); ++it) {
		LOG((CLOG_DEBUG "loading plugin: %s", (*it).c_str()));
		String path = String(dir).append("\\").append(*it);
		HINSTANCE library = LoadLibrary(path.c_str());

		if (library == NULL) {
			LOG((CLOG_ERR "failed to load plugin: %s %d", (*it).c_str(), GetLastError()));
			throw XArch(new XArchEvalWindows);
		}

		void* lib = reinterpret_cast<void*>(library);
		String filename = synergy::string::removeFileExt(*it);
		m_pluginTable.insert(std::make_pair(filename, lib));

		LOG((CLOG_DEBUG "loaded plugin: %s", (*it).c_str()));
	}
}
CString
CArchPluginWindows::getModuleDir()
{
	TCHAR c_modulePath[MAX_PATH];
	if (GetModuleFileName(NULL, c_modulePath, MAX_PATH) == 0) {
		throw XArch(new XArchEvalWindows);
	}

	CString modulePath(c_modulePath);
	size_t lastSlash = modulePath.find_last_of("\\");

	if (lastSlash != CString::npos) {
		return modulePath.substr(0, lastSlash);
	}

	throw XArch("could not get module path.");
}
Exemple #6
0
CAppUtilWindows::CAppUtilWindows() :
    m_exitMode(kExitModeNormal)
{
    if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE)
    {
        throw XArch(new XArchEvalWindows());
    }
}
Exemple #7
0
void
ArchSystemWindows::setting(const std::string& valueName, const std::string& valueString) const
{
	HKEY key = ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
	if (key == NULL)
		throw XArch(std::string("could not access registry key: ") + valueName);
	ArchMiscWindows::setValue(key, valueName.c_str(), valueString.c_str());
}
void
MSWindowsWatchdog::startProcess()
{
	if (m_command.empty()) {
		throw XMSWindowsWatchdogError("cannot start process, command is empty");
	}

	m_commandChanged = false;

	if (m_processRunning) {
		LOG((CLOG_DEBUG "closing existing process to make way for new one"));
		shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20);
		m_processRunning = false;
	}

	m_session.updateActiveSession();

	SECURITY_ATTRIBUTES sa;
	ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));

	getActiveDesktop(&sa);

	ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
	HANDLE userToken = getUserToken(&sa);
	m_autoElevated = false;

	// patch by Jack Zhou and Henry Tung
	// set UIAccess to fix Windows 8 GUI interaction
	// http://synergy-project.org/spit/issues/details/3338/#c70
	DWORD uiAccess = 1;
	SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD));

	BOOL createRet = doStartProcess(m_command, userToken, &sa);

	if (!createRet) {
		LOG((CLOG_ERR "could not launch"));
		DWORD exitCode = 0;
		GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
		LOG((CLOG_ERR "exit code: %d", exitCode));
		throw XArch(new XArchEvalWindows);
	}
	else {
		// wait for program to fail.
		ARCH->sleep(1);
		if (!isProcessActive()) {
			throw XMSWindowsWatchdogError("process immediately stopped");
		}

		m_processRunning = true;
		m_processFailures = 0;

		LOG((CLOG_DEBUG "started process, session=%i, command=%s",
			m_session.getActiveSessionId(), m_command.c_str()));
	}
}
HANDLE 
MSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security)
{
	HANDLE sourceToken;
	if (!WTSQueryUserToken(m_activeSessionId, &sourceToken)) {
		LOG((CLOG_ERR "could not get token from session %d", m_activeSessionId));
		throw XArch(new XArchEvalWindows);
	}
	
	HANDLE newToken;
	if (!DuplicateTokenEx(
		sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
		SecurityImpersonation, TokenPrimary, &newToken)) {

		LOG((CLOG_ERR "could not duplicate token"));
		throw XArch(new XArchEvalWindows);
	}
	
	LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
	return newToken;
}
void
CArchPluginWindows::load(const CString& dllFilename)
{
	LOG((CLOG_DEBUG "loading plugin: %s", dllFilename.c_str()));
	CString path = CString(getPluginsDir()).append("\\").append(dllFilename);
	HINSTANCE library = LoadLibrary(path.c_str());
	if (library == NULL)
		throw XArch(new XArchEvalWindows);

	initFunc initPlugin = (initFunc)GetProcAddress(library, "init");
	initPlugin(&sendEvent, &log);
}
void
WinINetRequest::connect()
{
    m_connect = InternetConnect(
        m_session,
        m_url.m_host.c_str(),
        m_url.m_port,
        NULL,
        NULL,
        INTERNET_SERVICE_HTTP,
        NULL,
        NULL);
    
    if (m_connect == NULL) {
        throw XArch(new XArchEvalWindows());
    }
}
void
WinINetRequest::openRequest()
{
    m_request = HttpOpenRequest(
        m_connect,
        "GET",
        m_url.m_path.c_str(),
        HTTP_VERSION,
        NULL,
        NULL,
        m_url.m_flags,
        NULL);

    if (m_request == NULL) {
        throw XArch(new XArchEvalWindows());
    }
}
BOOL
MSWindowsSession::nextProcessEntry(HANDLE snapshot, LPPROCESSENTRY32 entry)
{
	BOOL gotEntry = Process32Next(snapshot, entry);
	if (!gotEntry) {

		DWORD err = GetLastError();
		if (err != ERROR_NO_MORE_FILES) {

			// only worry about error if it's not the end of the snapshot
			LOG((CLOG_ERR "could not get next process entry"));
			throw XArch(new XArchEvalWindows());
		}
	}

	return gotEntry;
}
void
WinINetRequest::openSession()
{
    std::stringstream userAgent;
    userAgent << "Synergy ";
    userAgent << kVersion;

    m_session = InternetOpen(
        userAgent.str().c_str(),
        INTERNET_OPEN_TYPE_PRECONFIG,
        NULL,
        NULL,
        NULL);

    if (m_session == NULL) {
        throw XArch(new XArchEvalWindows());
    }
}
String
ArchInternetWindows::urlEncode(const String& url)
{
    TCHAR buffer[1024];
    DWORD bufferSize = sizeof(buffer);

    if (UrlEscape(url.c_str(), buffer, &bufferSize, URL_ESCAPE_UNSAFE) != S_OK) {
        throw XArch(new XArchEvalWindows());
    }

    String result(buffer);

    // the win32 url encoding funcitons are pretty useless (to us) and only
    // escape "unsafe" chars, but not + or =, so we need to replace these
    // manually (and probably many other chars).
    synergy::string::findReplaceAll(result, "+", "%2B");
    synergy::string::findReplaceAll(result, "=", "%3D");

    return result;
}
Exemple #16
0
BOOL
MSWindowsWatchdog::doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
{
	// clear, as we're reusing process info struct
	ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));

	STARTUPINFO si;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe?
	si.hStdError = m_stdOutWrite;
	si.hStdOutput = m_stdOutWrite;
	si.dwFlags |= STARTF_USESTDHANDLES;

	LPVOID environment;
	BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
	if (!blockRet) {
		LOG((CLOG_ERR "could not create environment block"));
		throw XArch(new XArchEvalWindows);
	}

	DWORD creationFlags = 
		NORMAL_PRIORITY_CLASS |
		CREATE_NO_WINDOW |
		CREATE_UNICODE_ENVIRONMENT;

	// re-launch in current active user session
	LOG((CLOG_INFO "starting new process"));
	BOOL createRet = CreateProcessAsUser(
		userToken, NULL, LPSTR(command.c_str()),
		sa, NULL, TRUE, creationFlags,
		environment, NULL, &si, &m_processInfo);

	DestroyEnvironmentBlock(environment);
	CloseHandle(userToken);

	return createRet;
}
Exemple #17
0
COSXScreen::COSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCursor) :
	CPlatformScreen(events),
	m_events(events),
	MouseButtonEventMap(NumButtonIDs),
	m_isPrimary(isPrimary),
	m_isOnScreen(m_isPrimary),
	m_cursorPosValid(false),
	m_cursorHidden(false),
	m_dragNumButtonsDown(0),
	m_dragTimer(NULL),
	m_keyState(NULL),
	m_sequenceNumber(0),
	m_screensaver(NULL),
	m_screensaverNotify(false),
	m_ownClipboard(false),
	m_clipboardTimer(NULL),
	m_hiddenWindow(NULL),
	m_userInputWindow(NULL),
	m_switchEventHandlerRef(0),
	m_pmMutex(new CMutex),
	m_pmWatchThread(NULL),
	m_pmThreadReady(new CCondVar<bool>(m_pmMutex, false)),
	m_activeModifierHotKey(0),
	m_activeModifierHotKeyMask(0),
	m_lastSingleClick(0),
	m_lastDoubleClick(0),
	m_lastSingleClickXCursor(0),
	m_lastSingleClickYCursor(0),
	m_autoShowHideCursor(autoShowHideCursor),
	m_eventTapRLSR(nullptr),
	m_eventTapPort(nullptr),
	m_pmRootPort(0)
{
	try {
		m_displayID   = CGMainDisplayID();
		updateScreenShape(m_displayID, 0);
		m_screensaver = new COSXScreenSaver(m_events, getEventTarget());
		m_keyState	  = new COSXKeyState(m_events);
		
    // TODO: http://stackoverflow.com/questions/2950124/enable-access-for-assistive-device-programmatically
		if (m_isPrimary && !AXAPIEnabled())
			throw XArch("system setting not enabled: \"Enable access for assistive devices\"");
		
		// install display manager notification handler
#if defined(MAC_OS_X_VERSION_10_5)
		CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this);
#else
		m_displayManagerNotificationUPP =
			NewDMExtendedNotificationUPP(displayManagerCallback);
		OSStatus err = GetCurrentProcess(&m_PSN);
		err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP,
							this, 0, &m_PSN);
#endif

		// install fast user switching event handler
		EventTypeSpec switchEventTypes[2];
		switchEventTypes[0].eventClass = kEventClassSystem;
		switchEventTypes[0].eventKind  = kEventSystemUserSessionDeactivated;
		switchEventTypes[1].eventClass = kEventClassSystem;
		switchEventTypes[1].eventKind  = kEventSystemUserSessionActivated;
		EventHandlerUPP switchEventHandler =
			NewEventHandlerUPP(userSwitchCallback);
		InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes,
									   this, &m_switchEventHandlerRef);
		DisposeEventHandlerUPP(switchEventHandler);

		constructMouseButtonEventMap();

		// watch for requests to sleep
		m_events->adoptHandler(m_events->forCOSXScreen().confirmSleep(),
								getEventTarget(),
								new TMethodEventJob<COSXScreen>(this,
									&COSXScreen::handleConfirmSleep));

		// create thread for monitoring system power state.
		*m_pmThreadReady = false;
		LOG((CLOG_DEBUG "starting watchSystemPowerThread"));
		m_pmWatchThread = new CThread(new TMethodJob<COSXScreen>
								(this, &COSXScreen::watchSystemPowerThread));
	}
	catch (...) {
		m_events->removeHandler(m_events->forCOSXScreen().confirmSleep(),
								getEventTarget());
		if (m_switchEventHandlerRef != 0) {
			RemoveEventHandler(m_switchEventHandlerRef);
		}
#if defined(MAC_OS_X_VERSION_10_5)
		CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
#else
		if (m_displayManagerNotificationUPP != NULL) {
			DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP,
							NULL, &m_PSN, 0);
		}

		if (m_hiddenWindow) {
			ReleaseWindow(m_hiddenWindow);
			m_hiddenWindow = NULL;
		}

		if (m_userInputWindow) {
			ReleaseWindow(m_userInputWindow);
			m_userInputWindow = NULL;
		}
#endif

		delete m_keyState;
		delete m_screensaver;
		throw;
	}

	// install event handlers
	m_events->adoptHandler(CEvent::kSystem, m_events->getSystemTarget(),
							new TMethodEventJob<COSXScreen>(this,
								&COSXScreen::handleSystemEvent));

	// install the platform event queue
	m_events->adoptBuffer(new COSXEventQueueBuffer(m_events));
}
void
CMSWindowsRelauncher::mainLoop(void*)
{
	SendSas sendSasFunc = NULL;
	HINSTANCE sasLib = LoadLibrary("sas.dll");
	if (sasLib) {
		LOG((CLOG_DEBUG "found sas.dll"));
		sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
	}

	DWORD sessionId = -1;
	bool launched = false;

	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());
	}

	PROCESS_INFORMATION pi;
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

	while (m_running) {

		HANDLE sendSasEvent = 0;
		if (sasLib && sendSasFunc) {
			// can't we just create one event? seems weird creating a new
			// event every second...
			sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS");
		}

		DWORD newSessionId = getSessionId();

		// only enter here when id changes, and the session isn't -1, which
		// may mean that there is no active session.
		if (((newSessionId != sessionId) && (newSessionId != -1)) || m_commandChanged) {
			
			m_commandChanged = false;

			if (launched) {
				LOG((CLOG_DEBUG "closing existing process to make way for new one"));
				shutdownProcess(pi, 10);
				launched = false;
			}

			// ok, this is now the active session (forget the old one if any)
			sessionId = newSessionId;

			SECURITY_ATTRIBUTES sa;
			ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));

			// get the token for the user in active session, which is the
			// one receiving input from mouse and keyboard.
			HANDLE userToken = getCurrentUserToken(sessionId, &sa);

			if (userToken != 0) {
				LOG((CLOG_DEBUG "got user token to launch new process"));

				std::string cmd = command();
				if (cmd == "") {
					LOG((CLOG_WARN "nothing to launch, no command specified."));
					continue;
				}

				// in case reusing process info struct
				ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

				STARTUPINFO si;
				ZeroMemory(&si, sizeof(STARTUPINFO));
				si.cb = sizeof(STARTUPINFO);
				si.lpDesktop = "winsta0\\default";
				si.hStdError = m_stdOutWrite;
				si.hStdOutput = m_stdOutWrite;
				si.dwFlags |= STARTF_USESTDHANDLES;

				LPVOID environment;
				BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
				if (!blockRet) {
					LOG((CLOG_ERR "could not create environment block (error: %i)", 
						GetLastError()));
					continue;
				}
				else {

					DWORD creationFlags = 
						NORMAL_PRIORITY_CLASS |
						CREATE_NO_WINDOW |
						CREATE_UNICODE_ENVIRONMENT;

					// re-launch in current active user session
					BOOL createRet = CreateProcessAsUser(
						userToken, NULL, LPSTR(cmd.c_str()),
						&sa, NULL, TRUE, creationFlags,
						environment, NULL, &si, &pi);

					DestroyEnvironmentBlock(environment);
					CloseHandle(userToken);

					if (!createRet) {
						LOG((CLOG_ERR "could not launch (error: %i)", GetLastError()));
						continue;
					}
					else {
						LOG((CLOG_DEBUG "launched in session %i (cmd: %s)", 
							sessionId, cmd.c_str()));
						launched = true;
					}
				}
			}
		}

		if (sendSasEvent) {
			// use SendSAS event to wait for next session.
			if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0 && sendSasFunc) {
				LOG((CLOG_DEBUG "calling SendSAS"));
				sendSasFunc(FALSE);
			}
			CloseHandle(sendSasEvent);
		}
		else {
			// check for session change every second.
			ARCH->sleep(1);
		}
	}

	if (launched) {
		LOG((CLOG_DEBUG "terminated running process on exit"));
		shutdownProcess(pi, 10);
	}
}
Exemple #19
0
void
MSWindowsWatchdog::mainLoop(void*)
{
	shutdownExistingProcesses();

	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; 

	if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) {
		throw XArch(new XArchEvalWindows());
	}

	ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));

	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_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_processRunning && !isProcessActive()) {

				m_processFailures++;
				m_processRunning = false;
			
				LOG((CLOG_WARN "detected application not running, pid=%d",
					m_processInfo.dwProcessId));
			}

			if (sendSasFunc != NULL) {

				HANDLE sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS");
				if (sendSasEvent != NULL) {

					// use SendSAS event to wait for next session (timeout 1 second).
					if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0) {
						LOG((CLOG_DEBUG "calling SendSAS"));
						sendSasFunc(FALSE);
					}

					CloseHandle(sendSasEvent);
					continue;
				}
			}

			// if the sas event failed, wait by sleeping.
			ARCH->sleep(1);
		
		}
		catch (std::exception& e) {
			LOG((CLOG_ERR "failed to launch, error: %s", e.what()));
			m_processFailures++;
			m_processRunning = false;
			continue;
		}
		catch (...) {
			LOG((CLOG_ERR "failed to launch, unknown error."));
			m_processFailures++;
			m_processRunning = false;
			continue;
		}
	}

	if (m_processRunning) {
		LOG((CLOG_DEBUG "terminated running process on exit"));
		shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20);
	}
	
	LOG((CLOG_DEBUG "watchdog main thread finished"));
}
Exemple #20
0
void
CMSWindowsRelauncher::mainLoop(void*)
{
	shutdownExistingProcesses();

	SendSas sendSasFunc = NULL;
	HINSTANCE sasLib = LoadLibrary("sas.dll");
	if (sasLib) {
		LOG((CLOG_DEBUG "found sas.dll"));
		sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
	}

	DWORD sessionId = -1;
	bool launched = false;

	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());
	}

	PROCESS_INFORMATION pi;
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

	int failures = 0;

	while (m_running) {

		HANDLE sendSasEvent = 0;
		if (sasLib && sendSasFunc) {
			// can't we just create one event? seems weird creating a new
			// event every second...
			sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS");
		}

		DWORD newSessionId = getSessionId();

		bool running = false;
		if (launched) {

			DWORD exitCode;
			GetExitCodeProcess(pi.hProcess, &exitCode);
			running = (exitCode == STILL_ACTIVE);

			if (!running) {
				failures++;
				LOG((CLOG_INFO "detected application not running, pid=%d, failures=%d", pi.dwProcessId, failures));
				
				// increasing backoff period, maximum of 10 seconds.
				int timeout = (failures * 2) < 10 ? (failures * 2) : 10;
				LOG((CLOG_DEBUG "waiting, backoff period is %d seconds", timeout));
				ARCH->sleep(timeout);
				
				// double check, in case process started after we waited.
				GetExitCodeProcess(pi.hProcess, &exitCode);
				running = (exitCode == STILL_ACTIVE);
			}
			else {
				// reset failures when running.
				failures = 0;
			}
		}

		// only enter here when id changes, and the session isn't -1, which
		// may mean that there is no active session.
		bool sessionChanged = ((newSessionId != sessionId) && (newSessionId != -1));

		// relaunch if it was running but has stopped unexpectedly.
		bool stoppedRunning = (launched && !running);

		if (stoppedRunning || sessionChanged || m_commandChanged) {
			
			m_commandChanged = false;

			if (launched) {
				LOG((CLOG_DEBUG "closing existing process to make way for new one"));
				shutdownProcess(pi.hProcess, pi.dwProcessId, 20);
				launched = false;
			}

			// ok, this is now the active session (forget the old one if any)
			sessionId = newSessionId;

			SECURITY_ATTRIBUTES sa;
			ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));

			// get the token for the user in active session, which is the
			// one receiving input from mouse and keyboard.
			HANDLE userToken = getCurrentUserToken(sessionId, &sa);

			if (userToken != 0) {
				LOG((CLOG_DEBUG "got user token to launch new process"));

				std::string cmd = command();
				if (cmd == "") {
					// this appears on first launch when the user hasn't configured
					// anything yet, so don't show it as a warning, only show it as
					// debug to devs to let them know why nothing happened.
					LOG((CLOG_DEBUG "nothing to launch, no command specified."));
					continue;
				}

				// in case reusing process info struct
				ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

				STARTUPINFO si;
				ZeroMemory(&si, sizeof(STARTUPINFO));
				si.cb = sizeof(STARTUPINFO);
				si.lpDesktop = "winsta0\\default";
				si.hStdError = m_stdOutWrite;
				si.hStdOutput = m_stdOutWrite;
				si.dwFlags |= STARTF_USESTDHANDLES;

				LPVOID environment;
				BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
				if (!blockRet) {
					LOG((CLOG_ERR "could not create environment block (error: %i)", 
						GetLastError()));
					continue;
				}
				else {

					DWORD creationFlags = 
						NORMAL_PRIORITY_CLASS |
						CREATE_NO_WINDOW |
						CREATE_UNICODE_ENVIRONMENT;

					// re-launch in current active user session
					LOG((CLOG_INFO "starting new process"));
					BOOL createRet = CreateProcessAsUser(
						userToken, NULL, LPSTR(cmd.c_str()),
						&sa, NULL, TRUE, creationFlags,
						environment, NULL, &si, &pi);

					DestroyEnvironmentBlock(environment);
					CloseHandle(userToken);

					if (!createRet) {
						LOG((CLOG_ERR "could not launch (error: %i)", GetLastError()));
						continue;
					}
					else {
						LOG((CLOG_DEBUG "launched in session %i (cmd: %s)", 
							sessionId, cmd.c_str()));
						launched = true;
					}
				}
			}
		}

		if (sendSasEvent) {
			// use SendSAS event to wait for next session.
			if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0 && sendSasFunc) {
				LOG((CLOG_DEBUG "calling SendSAS"));
				sendSasFunc(FALSE);
			}
			CloseHandle(sendSasEvent);
		}
		else {
			// check for session change every second.
			ARCH->sleep(1);
		}
	}

	if (launched) {
		LOG((CLOG_DEBUG "terminated running process on exit"));
		shutdownProcess(pi.hProcess, pi.dwProcessId, 20);
	}
	
	LOG((CLOG_DEBUG "relauncher main thread finished"));
}
bool
MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL)
{
	// first we need to take a snapshot of the running processes
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (snapshot == INVALID_HANDLE_VALUE) {
		LOG((CLOG_ERR "could not get process snapshot"));
		throw XArch(new XArchEvalWindows());
	}

	PROCESSENTRY32 entry;
	entry.dwSize = sizeof(PROCESSENTRY32);

	// get the first process, and if we can't do that then it's 
	// unlikely we can go any further
	BOOL gotEntry = Process32First(snapshot, &entry);
	if (!gotEntry) {
		LOG((CLOG_ERR "could not get first process entry"));
		throw XArch(new XArchEvalWindows());
	}

	// used to record process names for debug info
	std::list<std::string> nameList;

	// now just iterate until we can find winlogon.exe pid
	DWORD pid = 0;
	while(gotEntry) {

		// make sure we're not checking the system process
		if (entry.th32ProcessID != 0) {

			DWORD processSessionId;
			BOOL pidToSidRet = ProcessIdToSessionId(
				entry.th32ProcessID, &processSessionId);

			if (!pidToSidRet) {
				// if we can not acquire session associated with a specified process,
				// simply ignore it
				LOG((CLOG_ERR "could not get session id for process id %i", entry.th32ProcessID));
				gotEntry = nextProcessEntry(snapshot, &entry);
				continue;
			}
			else {
				// only pay attention to processes in the active session
				if (processSessionId == m_activeSessionId) {

					// store the names so we can record them for debug
					nameList.push_back(entry.szExeFile);

					if (_stricmp(entry.szExeFile, name) == 0) {
						pid = entry.th32ProcessID;
					}
				}
			}

		}

		// now move on to the next entry (if we're not at the end)
		gotEntry = nextProcessEntry(snapshot, &entry);
	}

	std::string nameListJoin;
	for(std::list<std::string>::iterator it = nameList.begin();
		it != nameList.end(); it++) {
			nameListJoin.append(*it);
			nameListJoin.append(", ");
	}

	LOG((CLOG_DEBUG "processes in session %d: %s",
		m_activeSessionId, nameListJoin.c_str()));

	CloseHandle(snapshot);

	if (pid) {
		if (process != NULL) {
			// now get the process, which we'll use to get the process token.
			LOG((CLOG_DEBUG "found %s in session %i", name, m_activeSessionId));
			*process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
		}
		return true;
	}
	else {
		LOG((CLOG_DEBUG "did not find %s in session %i", name, m_activeSessionId));
		return false;
	}
}