aboutsummaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authorSamuel Mannehed <samuel@cendio.se>2014-02-07 14:53:24 +0000
committerSamuel Mannehed <samuel@cendio.se>2014-02-07 14:53:24 +0000
commit60c419320d2229ace017d1f599b6ab0a7b9e2927 (patch)
tree55eeba27730e7845b324ce748985712b2ae91070 /win
parentdc6af3740c36ec18e1c409096d6943951982d7ec (diff)
downloadtigervnc-60c419320d2229ace017d1f599b6ab0a7b9e2927.tar.gz
tigervnc-60c419320d2229ace017d1f599b6ab0a7b9e2927.zip
Make WinVNC service mode work on Windows Vista and beyond.
Patch by Jochen Tucht, fixes bug 135. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@5158 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'win')
-rw-r--r--win/rfb_win32/LaunchProcess.cxx10
-rw-r--r--win/rfb_win32/Service.cxx10
-rw-r--r--win/winvnc/STrayIcon.cxx17
-rw-r--r--win/winvnc/VNCServerService.cxx144
-rw-r--r--win/winvnc/VNCServerService.h8
-rw-r--r--win/winvnc/VNCServerWin32.cxx6
-rw-r--r--win/winvnc/VNCServerWin32.h1
-rw-r--r--win/winvnc/winvnc.cxx22
8 files changed, 180 insertions, 38 deletions
diff --git a/win/rfb_win32/LaunchProcess.cxx b/win/rfb_win32/LaunchProcess.cxx
index 56a712e6..16ced64b 100644
--- a/win/rfb_win32/LaunchProcess.cxx
+++ b/win/rfb_win32/LaunchProcess.cxx
@@ -44,10 +44,20 @@ void LaunchProcess::start(HANDLE userToken, bool createConsole) {
await();
returnCode = STILL_ACTIVE;
+ DWORD size;
+ char desktopName[256];
+ char buf[256];
+ HDESK desktop = GetThreadDesktop(GetCurrentThreadId());
+ if (!GetUserObjectInformation(desktop, UOI_NAME, buf, 256, &size))
+ throw rdr::SystemException("unable to launch process", GetLastError());
+
+ snprintf(desktopName, 256, "WinSta0\\%s", buf);
+
// - Create storage for the process startup information
STARTUPINFO sinfo;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
+ sinfo.lpDesktop = desktopName;
// - Concoct a suitable command-line
TCharArray exePath;
diff --git a/win/rfb_win32/Service.cxx b/win/rfb_win32/Service.cxx
index 982a5b36..89be92e1 100644
--- a/win/rfb_win32/Service.cxx
+++ b/win/rfb_win32/Service.cxx
@@ -40,6 +40,7 @@ static LogWriter vlog("Service");
// - Internal service implementation functions
Service* service = 0;
+bool runAsService = false;
VOID WINAPI serviceHandler(DWORD control) {
switch (control) {
@@ -323,6 +324,13 @@ rfb::win32::emulateCtrlAltDel() {
if (!osVersion.isPlatformNT)
return false;
+ if (osVersion.dwMajorVersion >= 6) {
+ rfb::win32::Handle sessionEventCad =
+ CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad");
+ SetEvent(sessionEventCad);
+ return true;
+ }
+
CADThread* cad_thread = new CADThread();
vlog.debug("emulate Ctrl-Alt-Del");
if (cad_thread) {
@@ -641,5 +649,5 @@ char* rfb::win32::serviceStateName(DWORD state) {
bool rfb::win32::isServiceProcess() {
- return service != 0;
+ return runAsService;
}
diff --git a/win/winvnc/STrayIcon.cxx b/win/winvnc/STrayIcon.cxx
index 354575ad..84575bd0 100644
--- a/win/winvnc/STrayIcon.cxx
+++ b/win/winvnc/STrayIcon.cxx
@@ -136,22 +136,10 @@ public:
}
break;
case ID_OPTIONS:
- {
- CurrentUserToken token;
- if (token.canImpersonate())
- vncConfig.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE);
- else
- vlog.error("Options: unknown current user");
- }
+ vncConfig.start(INVALID_HANDLE_VALUE);
break;
case ID_CONNECT:
- {
- CurrentUserToken token;
- if (token.canImpersonate())
- vncConnect.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE);
- else
- vlog.error("Options: unknown current user");
- }
+ vncConnect.start(INVALID_HANDLE_VALUE);
break;
case ID_DISCONNECT:
thread.server.disconnectClients("tray menu disconnect");
@@ -160,7 +148,6 @@ public:
if (MsgBox(0, _T("Are you sure you want to close the server?"),
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDYES) {
if (isServiceProcess()) {
- ImpersonateCurrentUser icu;
try {
rfb::win32::stopService(VNCServerService::Name);
} catch (rdr::Exception& e) {
diff --git a/win/winvnc/VNCServerService.cxx b/win/winvnc/VNCServerService.cxx
index 2ef2ee08..1a2a8e93 100644
--- a/win/winvnc/VNCServerService.cxx
+++ b/win/winvnc/VNCServerService.cxx
@@ -20,6 +20,10 @@
#include <winvnc/VNCServerService.h>
#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/TsSessions.h>
+#include <rfb_win32/ModuleFileName.h>
+#include <wtsapi32.h>
+#include <tlhelp32.h>
using namespace winvnc;
using namespace rfb;
@@ -27,9 +31,12 @@ using namespace win32;
const TCHAR* winvnc::VNCServerService::Name = _T("WinVNC4");
-
-VNCServerService::VNCServerService(VNCServerWin32& s)
- : Service(Name), server(s) {
+VNCServerService::VNCServerService()
+ : Service(Name)
+ , SendSas(_T("sas.dll"), "SendSAS")
+ , stopServiceEvent(CreateEvent(0, FALSE, FALSE, 0))
+ , sessionEvent(CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC"))
+ , sessionEventCad(CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad")) {
// - Set the service-mode logging defaults
// These will be overridden by the Log option in the
// registry, if present.
@@ -40,13 +47,132 @@ VNCServerService::VNCServerService(VNCServerWin32& s)
}
-DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[]) {
- setStatus(SERVICE_RUNNING);
- int result = server.run();
- setStatus(SERVICE_STOP_PENDING);
- return result;
+//////////////////////////////////////////////////////////////////////////////
+
+DWORD GetLogonPid(DWORD dwSessionId)
+{
+ DWORD dwLogonPid = 0;
+ HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hSnap != INVALID_HANDLE_VALUE)
+ {
+ PROCESSENTRY32 procEntry;
+ procEntry.dwSize = sizeof procEntry;
+
+ if (Process32First(hSnap, &procEntry)) do
+ {
+ DWORD dwLogonSessionId = 0;
+ if (_stricmp(procEntry.szExeFile, "winlogon.exe") == 0 &&
+ ProcessIdToSessionId(procEntry.th32ProcessID, &dwLogonSessionId) &&
+ dwLogonSessionId == dwSessionId)
+ {
+ dwLogonPid = procEntry.th32ProcessID;
+ break;
+ }
+ } while (Process32Next(hSnap, &procEntry));
+ CloseHandle(hSnap);
+ }
+ return dwLogonPid;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+BOOL GetSessionUserTokenWin(OUT LPHANDLE lphUserToken)
+{
+ BOOL bResult = FALSE;
+ ConsoleSessionId ID_session;
+ DWORD Id = GetLogonPid(ID_session.id);
+ if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Id))
+ {
+ bResult = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, lphUserToken);
+ CloseHandle(hProcess);
+ }
+ return bResult;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// START the app as system
+HANDLE LaunchProcessWin(DWORD dwSessionId)
+{
+ HANDLE hProcess = NULL;
+ HANDLE hToken = NULL;
+ if (GetSessionUserTokenWin(&hToken))
+ {
+ ModuleFileName filename;
+ static const char cmdLineFmt[] = "\"%s\" -noconsole -service_run";
+ TCharArray cmdLine(_tcslen(filename.buf) + sizeof(cmdLineFmt)/sizeof(cmdLineFmt[0]));
+ _stprintf(cmdLine.buf, cmdLineFmt, filename.buf);
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof si);
+ si.cb = sizeof si;
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ PROCESS_INFORMATION pi;
+ if (CreateProcessAsUser(hToken, NULL, cmdLine.buf, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
+ {
+ CloseHandle(pi.hThread);
+ hProcess = pi.hProcess;
+ }
+ CloseHandle(hToken);
+ }
+ return hProcess;
+}
+
+DWORD VNCServerService::serviceMain(int argc, TCHAR* argv[])
+{
+ ConsoleSessionId OlddwSessionId;
+
+ HANDLE hProcess = NULL;
+ //We use this event to notify the program that the session has changed
+ //The program need to end so the service can restart the program in the correct session
+ //wait_for_existing_process();
+ HANDLE testevent[2] = { stopServiceEvent, sessionEventCad };
+ setStatus(SERVICE_RUNNING);
+ while (status.dwCurrentState == SERVICE_RUNNING)
+ {
+ DWORD dwEvent = WaitForMultipleObjects(2, testevent, FALSE, 1000);
+ switch (dwEvent)
+ {
+ //stopServiceEvent, exit while loop
+ case WAIT_OBJECT_0 + 0:
+ setStatus(SERVICE_STOP_PENDING);
+ break;
+
+ //cad request
+ case WAIT_OBJECT_0 + 1:
+ if (SendSas.isValid())
+ (*SendSas)(FALSE);
+ break;
+
+ case WAIT_TIMEOUT:
+ {
+ ConsoleSessionId dwSessionId;
+ if (OlddwSessionId.id != dwSessionId.id)
+ {
+ OlddwSessionId.id = dwSessionId.id;
+ SetEvent(sessionEvent);
+ }
+ DWORD dwExitCode = 0;
+ if (hProcess == NULL ||
+ (GetExitCodeProcess(hProcess, &dwExitCode) &&
+ dwExitCode != STILL_ACTIVE &&
+ CloseHandle(hProcess)))
+ {
+ hProcess = LaunchProcessWin(dwSessionId.id);
+ }
+ }
+ break;
+ }
+ }
+
+ SetEvent(sessionEvent);
+
+ if (hProcess)
+ {
+ WaitForSingleObject(hProcess, 15000);
+ CloseHandle(hProcess);
+ }
+ return 0;
}
void VNCServerService::stop() {
- server.stop();
+ SetEvent(stopServiceEvent);
+ SetEvent(sessionEvent);
}
diff --git a/win/winvnc/VNCServerService.h b/win/winvnc/VNCServerService.h
index c7a76cce..a55a7294 100644
--- a/win/winvnc/VNCServerService.h
+++ b/win/winvnc/VNCServerService.h
@@ -21,19 +21,23 @@
#include <winvnc/VNCServerWin32.h>
#include <rfb_win32/Service.h>
+#include <rfb_win32/DynamicFn.h>
namespace winvnc {
class VNCServerService : public rfb::win32::Service {
public:
- VNCServerService(VNCServerWin32& s);
+ VNCServerService();
DWORD serviceMain(int argc, TCHAR* argv[]);
void stop();
static const TCHAR* Name;
protected:
- VNCServerWin32& server;
+ rfb::win32::DynamicFn<void (WINAPI *)(BOOL)> SendSas;
+ rfb::win32::Handle stopServiceEvent;
+ rfb::win32::Handle sessionEvent;
+ rfb::win32::Handle sessionEventCad;
};
};
diff --git a/win/winvnc/VNCServerWin32.cxx b/win/winvnc/VNCServerWin32.cxx
index 4d89a0ef..9d77c27c 100644
--- a/win/winvnc/VNCServerWin32.cxx
+++ b/win/winvnc/VNCServerWin32.cxx
@@ -55,6 +55,8 @@ static BoolParameter showTrayIcon("ShowTrayIcon",
VNCServerWin32::VNCServerWin32()
: command(NoCommand), commandSig(commandLock),
commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
+ sessionEvent(isServiceProcess() ?
+ CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
vncServer(CStr(ComputerName().buf), &desktop),
hostThread(0), runServer(false), isDesktopStarted(false),
httpServer(&vncServer), config(&sockMgr), trayIcon(0),
@@ -72,6 +74,8 @@ VNCServerWin32::VNCServerWin32()
// Register the queued command event to be handled
sockMgr.addEvent(commandEvent, this);
+ if (sessionEvent)
+ sockMgr.addEvent(sessionEvent, this);
}
VNCServerWin32::~VNCServerWin32() {
@@ -322,6 +326,8 @@ void VNCServerWin32::processEvent(HANDLE event_) {
command = NoCommand;
commandSig.signal();
}
+ } else if (event_ == sessionEvent.h) {
+ stop();
}
}
diff --git a/win/winvnc/VNCServerWin32.h b/win/winvnc/VNCServerWin32.h
index 5b40a5ab..1feae368 100644
--- a/win/winvnc/VNCServerWin32.h
+++ b/win/winvnc/VNCServerWin32.h
@@ -102,6 +102,7 @@ namespace winvnc {
rfb::Mutex commandLock;
rfb::Condition commandSig;
rfb::win32::Handle commandEvent;
+ rfb::win32::Handle sessionEvent;
// VNCServerWin32 Server-internal state
rfb::win32::SDisplay desktop;
diff --git a/win/winvnc/winvnc.cxx b/win/winvnc/winvnc.cxx
index 1df0f769..4aa5dcb6 100644
--- a/win/winvnc/winvnc.cxx
+++ b/win/winvnc/winvnc.cxx
@@ -43,7 +43,7 @@ static LogWriter vlog("main");
TStr rfb::win32::AppName("VNC Server");
-static bool runAsService = false;
+extern bool runAsService;
static bool runServer = true;
static bool close_console = false;
@@ -159,6 +159,11 @@ static void processParams(int argc, char** argv) {
MsgBoxOrLog(result.buf);
} else if (strcasecmp(argv[i], "-service") == 0) {
printf("Run in service mode\n");
+ runServer = false;
+ runAsService = true;
+
+ } else if (strcasecmp(argv[i], "-service_run") == 0) {
+ printf("Run in service mode\n");
runAsService = true;
} else if (strcasecmp(argv[i], "-register") == 0) {
@@ -255,16 +260,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdSho
if (runServer) {
// Start the network subsystem and run the server
VNCServerWin32 server;
-
- if (runAsService) {
- printf("Starting Service-Mode VNC Server.\n");
- VNCServerService service(server);
- service.start();
- result = service.getStatus().dwWin32ExitCode;
- } else {
- printf("Starting User-Mode VNC Server.\n");
- result = server.run();
- }
+ result = server.run();
+ } else if (runAsService) {
+ VNCServerService service;
+ service.start();
+ result = service.getStatus().dwWin32ExitCode;
}
vlog.debug("WinVNC service destroyed");