Blame SOURCES/tigervnc-systemd-support.patch

cfbdf6
From 8f09de712b53e54d15d2bd5367c10a61c57cda23 Mon Sep 17 00:00:00 2001
cfbdf6
From: Jan Grulich <jgrulich@redhat.com>
cfbdf6
Date: Wed, 6 May 2020 10:37:21 +0200
cfbdf6
Subject: Foo
cfbdf6
cfbdf6
cfbdf6
diff --git a/CMakeLists.txt b/CMakeLists.txt
cfbdf6
index 2d19b72..78eb93d 100644
cfbdf6
--- a/CMakeLists.txt
cfbdf6
+++ b/CMakeLists.txt
cfbdf6
@@ -7,6 +7,10 @@ if(POLICY CMP0022)
cfbdf6
   cmake_policy(SET CMP0022 OLD)
cfbdf6
 endif()
cfbdf6
 
cfbdf6
+if(${CMAKE_VERSION} VERSION_LESS "3.4.0")
cfbdf6
+  message(WARNING "CMake 3.4.0 or newer is required to get correct default installation paths")
cfbdf6
+endif()
cfbdf6
+
cfbdf6
 # Internal cmake modules
cfbdf6
 set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
cfbdf6
 
cfbdf6
@@ -27,17 +31,16 @@ set(VERSION 1.10.1)
cfbdf6
 set(RCVERSION 1,10,1,0)
cfbdf6
 
cfbdf6
 # Installation paths
cfbdf6
-set(BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin")
cfbdf6
-set(DATA_DIR "${CMAKE_INSTALL_PREFIX}/share")
cfbdf6
-set(MAN_DIR "${DATA_DIR}/man")
cfbdf6
-set(LOCALE_DIR "${DATA_DIR}/locale")
cfbdf6
-set(DOC_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${CMAKE_PROJECT_NAME}-${VERSION}")
cfbdf6
-
cfbdf6
-if(WIN32)
cfbdf6
-set(BIN_DIR "${CMAKE_INSTALL_PREFIX}")
cfbdf6
-set(DOC_DIR "${CMAKE_INSTALL_PREFIX}")
cfbdf6
+include(GNUInstallDirs)
cfbdf6
+set(CMAKE_INSTALL_UNITDIR "lib/systemd/system" CACHE PATH "systemd unit files (lib/systemd/system)")
cfbdf6
+if(IS_ABSOLUTE "${CMAKE_INSTALL_UNITDIR}")
cfbdf6
+  set(CMAKE_INSTALL_FULL_UNITDIR "${CMAKE_INSTALL_UNITDIR}")
cfbdf6
+else()
cfbdf6
+  set(CMAKE_INSTALL_FULL_UNITDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_UNITDIR}")
cfbdf6
 endif()
cfbdf6
 
cfbdf6
+option(INSTALL_SYSTEMD_UNITS "Install TigerVNC systemd units" ON)
cfbdf6
+
cfbdf6
 if(MSVC)
cfbdf6
   message(FATAL_ERROR "TigerVNC cannot be built with Visual Studio.  Please use MinGW")
cfbdf6
 endif()
cfbdf6
@@ -259,8 +262,7 @@ if(ENABLE_GNUTLS)
cfbdf6
 endif()
cfbdf6
 
cfbdf6
 # Check for PAM library
cfbdf6
-option(ENABLE_PAM "Enable PAM authentication support" ON)
cfbdf6
-if(ENABLE_PAM)
cfbdf6
+if(UNIX AND NOT APPLE)
cfbdf6
   check_include_files(security/pam_appl.h HAVE_PAM_H)
cfbdf6
   set(CMAKE_REQUIRED_LIBRARIES -lpam)
cfbdf6
   check_function_exists(pam_start HAVE_PAM_START)
cfbdf6
@@ -268,10 +270,9 @@ if(ENABLE_PAM)
cfbdf6
   if(HAVE_PAM_H AND HAVE_PAM_START)
cfbdf6
     set(PAM_LIBS pam)
cfbdf6
   else()
cfbdf6
-    set(ENABLE_PAM 0)
cfbdf6
+    message(FATAL_ERROR "Could not find PAM development files")
cfbdf6
   endif()
cfbdf6
 endif()
cfbdf6
-set(HAVE_PAM ${ENABLE_PAM})
cfbdf6
 
cfbdf6
 # Generate config.h and make sure the source finds it
cfbdf6
 configure_file(config.h.in config.h)
cfbdf6
diff --git a/cmake/BuildPackages.cmake b/cmake/BuildPackages.cmake
cfbdf6
index ec96318..1f25192 100644
cfbdf6
--- a/cmake/BuildPackages.cmake
cfbdf6
+++ b/cmake/BuildPackages.cmake
cfbdf6
@@ -86,5 +86,5 @@ endif() #UNIX
cfbdf6
 # Common
cfbdf6
 #
cfbdf6
 
cfbdf6
-install(FILES ${CMAKE_SOURCE_DIR}/LICENCE.TXT DESTINATION ${DOC_DIR})
cfbdf6
-install(FILES ${CMAKE_SOURCE_DIR}/README.rst DESTINATION ${DOC_DIR})
cfbdf6
+install(FILES ${CMAKE_SOURCE_DIR}/LICENCE.TXT DESTINATION ${CMAKE_INSTALL_FULL_DOCDIR})
cfbdf6
+install(FILES ${CMAKE_SOURCE_DIR}/README.rst DESTINATION ${CMAKE_INSTALL_FULL_DOCDIR})
cfbdf6
diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake
cfbdf6
index e539619..793b190 100644
cfbdf6
--- a/cmake/StaticBuild.cmake
cfbdf6
+++ b/cmake/StaticBuild.cmake
cfbdf6
@@ -115,7 +115,7 @@ endif()
cfbdf6
 if(BUILD_STATIC_GCC)
cfbdf6
   # This ensures that we don't depend on libstdc++ or libgcc_s
cfbdf6
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nodefaultlibs")
cfbdf6
-  set(STATIC_BASE_LIBRARIES "-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic")
cfbdf6
+  set(STATIC_BASE_LIBRARIES "")
cfbdf6
   if(ENABLE_ASAN AND NOT WIN32 AND NOT APPLE)
cfbdf6
     set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -Wl,-Bstatic -lasan -Wl,-Bdynamic -ldl -lm -lpthread")
cfbdf6
   endif()
cfbdf6
@@ -135,5 +135,6 @@ if(BUILD_STATIC_GCC)
cfbdf6
   else()
cfbdf6
     set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -lgcc -lgcc_eh -lc")
cfbdf6
   endif()
cfbdf6
-  set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${STATIC_BASE_LIBRARIES}")
cfbdf6
+  set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} ${STATIC_BASE_LIBRARIES}")
cfbdf6
+  set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic ${STATIC_BASE_LIBRARIES}")
cfbdf6
 endif()
cfbdf6
diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt
cfbdf6
index 8e532a2..689cdcc 100644
cfbdf6
--- a/common/rfb/CMakeLists.txt
cfbdf6
+++ b/common/rfb/CMakeLists.txt
cfbdf6
@@ -75,7 +75,7 @@ endif(WIN32)
cfbdf6
 
cfbdf6
 set(RFB_LIBRARIES ${JPEG_LIBRARIES} os rdr Xregion)
cfbdf6
 
cfbdf6
-if(HAVE_PAM)
cfbdf6
+if(UNIX AND NOT APPLE)
cfbdf6
   set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx
cfbdf6
     UnixPasswordValidator.h pam.c pam.h)
cfbdf6
   set(RFB_LIBRARIES ${RFB_LIBRARIES} ${PAM_LIBS})
cfbdf6
diff --git a/common/rfb/SSecurityPlain.cxx b/common/rfb/SSecurityPlain.cxx
cfbdf6
index 6f72432..f577c0d 100644
cfbdf6
--- a/common/rfb/SSecurityPlain.cxx
cfbdf6
+++ b/common/rfb/SSecurityPlain.cxx
cfbdf6
@@ -25,10 +25,10 @@
cfbdf6
 #include <rfb/SConnection.h>
cfbdf6
 #include <rfb/Exception.h>
cfbdf6
 #include <rdr/InStream.h>
cfbdf6
-#ifdef HAVE_PAM
cfbdf6
+#if !defined(WIN32) && !defined(__APPLE__)
cfbdf6
 #include <rfb/UnixPasswordValidator.h>
cfbdf6
 #endif
cfbdf6
-#ifdef BUILD_WIN
cfbdf6
+#ifdef WIN32
cfbdf6
 #include <rfb/WinPasswdValidator.h>
cfbdf6
 #endif
cfbdf6
 
cfbdf6
@@ -62,10 +62,10 @@ bool PasswordValidator::validUser(const char* username)
cfbdf6
 
cfbdf6
 SSecurityPlain::SSecurityPlain(SConnection* sc) : SSecurity(sc)
cfbdf6
 {
cfbdf6
-#ifdef HAVE_PAM
cfbdf6
-  valid = new UnixPasswordValidator();
cfbdf6
-#elif BUILD_WIN
cfbdf6
+#ifdef WIN32
cfbdf6
   valid = new WinPasswdValidator();
cfbdf6
+#elif !defined(__APPLE__)
cfbdf6
+  valid = new UnixPasswordValidator();
cfbdf6
 #else
cfbdf6
   valid = NULL;
cfbdf6
 #endif
cfbdf6
diff --git a/common/rfb/UnixPasswordValidator.cxx b/common/rfb/UnixPasswordValidator.cxx
cfbdf6
index d096079..ee7bc0d 100644
cfbdf6
--- a/common/rfb/UnixPasswordValidator.cxx
cfbdf6
+++ b/common/rfb/UnixPasswordValidator.cxx
cfbdf6
@@ -25,9 +25,7 @@
cfbdf6
 #include <rfb/Configuration.h>
cfbdf6
 #include <rfb/Exception.h>
cfbdf6
 #include <rfb/UnixPasswordValidator.h>
cfbdf6
-#ifdef HAVE_PAM
cfbdf6
 #include <rfb/pam.h>
cfbdf6
-#endif
cfbdf6
 
cfbdf6
 using namespace rfb;
cfbdf6
 
cfbdf6
@@ -43,10 +41,6 @@ bool UnixPasswordValidator::validateInternal(SConnection * sc,
cfbdf6
 					     const char *username,
cfbdf6
 					     const char *password)
cfbdf6
 {
cfbdf6
-#ifdef HAVE_PAM
cfbdf6
   CharArray service(strDup(pamService.getData()));
cfbdf6
   return do_pam_auth(service.buf, username, password);
cfbdf6
-#else
cfbdf6
-  throw AuthFailureException("PAM not supported");
cfbdf6
-#endif
cfbdf6
 }
cfbdf6
diff --git a/common/rfb/pam.c b/common/rfb/pam.c
cfbdf6
index cb067fd..acac0f4 100644
cfbdf6
--- a/common/rfb/pam.c
cfbdf6
+++ b/common/rfb/pam.c
cfbdf6
@@ -22,9 +22,6 @@
cfbdf6
 #include <config.h>
cfbdf6
 #endif
cfbdf6
 
cfbdf6
-#ifndef HAVE_PAM
cfbdf6
-#error "This source should not be compiled when PAM is unsupported"
cfbdf6
-#endif
cfbdf6
 
cfbdf6
 #include <stdlib.h>
cfbdf6
 #include <string.h>
cfbdf6
diff --git a/common/rfb/pam.h b/common/rfb/pam.h
cfbdf6
index 2688f21..d378d19 100644
cfbdf6
--- a/common/rfb/pam.h
cfbdf6
+++ b/common/rfb/pam.h
cfbdf6
@@ -21,14 +21,6 @@
cfbdf6
 #ifndef __RFB_PAM_H__
cfbdf6
 #define __RFB_PAM_H__
cfbdf6
 
cfbdf6
-#ifdef HAVE_CONFIG_H
cfbdf6
-#include <config.h>
cfbdf6
-#endif
cfbdf6
-
cfbdf6
-#ifndef HAVE_PAM
cfbdf6
-#error "This header should not be included when PAM is unsupported"
cfbdf6
-#endif
cfbdf6
-
cfbdf6
 #ifdef __cplusplus
cfbdf6
 extern "C" {
cfbdf6
 #endif
cfbdf6
diff --git a/config.h.in b/config.h.in
cfbdf6
index 2d6db9c..2d7a741 100644
cfbdf6
--- a/config.h.in
cfbdf6
+++ b/config.h.in
cfbdf6
@@ -4,10 +4,10 @@
cfbdf6
 #cmakedefine HAVE_ACTIVE_DESKTOP_H
cfbdf6
 #cmakedefine HAVE_ACTIVE_DESKTOP_L
cfbdf6
 #cmakedefine ENABLE_NLS 1
cfbdf6
-#cmakedefine HAVE_PAM
cfbdf6
 
cfbdf6
-#cmakedefine DATA_DIR "@DATA_DIR@"
cfbdf6
-#cmakedefine LOCALE_DIR "@LOCALE_DIR@"
cfbdf6
+#cmakedefine CMAKE_INSTALL_FULL_LIBEXECDIR "@CMAKE_INSTALL_FULL_LIBEXECDIR@"
cfbdf6
+#cmakedefine CMAKE_INSTALL_FULL_DATADIR "@CMAKE_INSTALL_FULL_DATADIR@"
cfbdf6
+#cmakedefine CMAKE_INSTALL_FULL_LOCALEDIR "@CMAKE_INSTALL_FULL_LOCALEDIR@"
cfbdf6
 
cfbdf6
 /* MS Visual Studio 2008 and newer doesn't know ssize_t */
cfbdf6
 #if defined(HAVE_GNUTLS) && defined(WIN32) && !defined(__MINGW32__)
cfbdf6
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
cfbdf6
index f61e355..77ec21b 100644
cfbdf6
--- a/java/CMakeLists.txt
cfbdf6
+++ b/java/CMakeLists.txt
cfbdf6
@@ -7,8 +7,6 @@ endif()
cfbdf6
 
cfbdf6
 find_package(Java)
cfbdf6
 
cfbdf6
-set(DATA_DIR "${CMAKE_INSTALL_PREFIX}/share")
cfbdf6
-
cfbdf6
 set(DEFAULT_JAVACFLAGS "-source 8 -target 8 -encoding UTF-8 -Xlint:all,-serial,-cast,-unchecked,-fallthrough,-dep-ann,-deprecation,-rawtypes")
cfbdf6
 set(JAVACFLAGS ${DEFAULT_JAVACFLAGS} CACHE STRING
cfbdf6
   "Java compiler flags (Default: ${DEFAULT_JAVACFLAGS})")
cfbdf6
diff --git a/media/CMakeLists.txt b/media/CMakeLists.txt
cfbdf6
index 256d435..088c72f 100644
cfbdf6
--- a/media/CMakeLists.txt
cfbdf6
+++ b/media/CMakeLists.txt
cfbdf6
@@ -13,11 +13,11 @@ if(CONVERT_EXECUTABLE)
cfbdf6
   if(UNIX AND NOT APPLE)
cfbdf6
     foreach(SIZE 16 22 24 32 48)
cfbdf6
       install(FILES icons/tigervnc_${SIZE}.png
cfbdf6
-        DESTINATION ${DATA_DIR}/icons/hicolor/${SIZE}x${SIZE}/apps
cfbdf6
+        DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/${SIZE}x${SIZE}/apps
cfbdf6
         RENAME tigervnc.png)
cfbdf6
     endforeach()
cfbdf6
     install(FILES icons/tigervnc.svg
cfbdf6
-      DESTINATION ${DATA_DIR}/icons/hicolor/scalable/apps)
cfbdf6
+      DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/scalable/apps)
cfbdf6
   endif()
cfbdf6
 endif()
cfbdf6
 
cfbdf6
diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt
cfbdf6
index 9c8ddef..2eb10e7 100644
cfbdf6
--- a/po/CMakeLists.txt
cfbdf6
+++ b/po/CMakeLists.txt
cfbdf6
@@ -46,7 +46,7 @@ foreach(lang ${po_FILES})
cfbdf6
   )
cfbdf6
 
cfbdf6
   install(FILES ${mo}
cfbdf6
-    DESTINATION "${LOCALE_DIR}/${lang}/LC_MESSAGES"
cfbdf6
+    DESTINATION "${CMAKE_INSTALL_FULL_LOCALEDIR}/${lang}/LC_MESSAGES"
cfbdf6
     RENAME tigervnc.mo
cfbdf6
   )
cfbdf6
 
cfbdf6
diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt
cfbdf6
index 7a1457d..5456e00 100644
cfbdf6
--- a/unix/CMakeLists.txt
cfbdf6
+++ b/unix/CMakeLists.txt
cfbdf6
@@ -2,7 +2,5 @@ add_subdirectory(tx)
cfbdf6
 add_subdirectory(common)
cfbdf6
 add_subdirectory(vncconfig)
cfbdf6
 add_subdirectory(vncpasswd)
cfbdf6
+add_subdirectory(vncserver)
cfbdf6
 add_subdirectory(x0vncserver)
cfbdf6
-
cfbdf6
-install(PROGRAMS vncserver DESTINATION ${BIN_DIR})
cfbdf6
-install(FILES vncserver.man DESTINATION ${MAN_DIR}/man1 RENAME vncserver.1)
cfbdf6
diff --git a/unix/vncconfig/CMakeLists.txt b/unix/vncconfig/CMakeLists.txt
cfbdf6
index 959681f..c3823ab 100644
cfbdf6
--- a/unix/vncconfig/CMakeLists.txt
cfbdf6
+++ b/unix/vncconfig/CMakeLists.txt
cfbdf6
@@ -11,5 +11,5 @@ add_executable(vncconfig
cfbdf6
 
cfbdf6
 target_link_libraries(vncconfig tx rfb network rdr ${X11_LIBRARIES})
cfbdf6
 
cfbdf6
-install(TARGETS vncconfig DESTINATION ${BIN_DIR})
cfbdf6
-install(FILES vncconfig.man DESTINATION ${MAN_DIR}/man1 RENAME vncconfig.1)
cfbdf6
+install(TARGETS vncconfig DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
cfbdf6
+install(FILES vncconfig.man DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1 RENAME vncconfig.1)
cfbdf6
diff --git a/unix/vncconfig/vncconfig.man b/unix/vncconfig/vncconfig.man
cfbdf6
index b685a46..ed9ddda 100644
cfbdf6
--- a/unix/vncconfig/vncconfig.man
cfbdf6
+++ b/unix/vncconfig/vncconfig.man
cfbdf6
@@ -111,8 +111,8 @@ When run as a "helper" app, make the window iconified at startup.
cfbdf6
 .SH SEE ALSO
cfbdf6
 .BR vncpasswd (1),
cfbdf6
 .BR vncviewer (1),
cfbdf6
-.BR vncserver (1),
cfbdf6
-.BR Xvnc (1)
cfbdf6
+.BR Xvnc (1),
cfbdf6
+.BR vncsession (8)
cfbdf6
 .br
cfbdf6
 https://www.tigervnc.org
cfbdf6
 
cfbdf6
diff --git a/unix/vncpasswd/CMakeLists.txt b/unix/vncpasswd/CMakeLists.txt
cfbdf6
index a04ed0b..9f716fa 100644
cfbdf6
--- a/unix/vncpasswd/CMakeLists.txt
cfbdf6
+++ b/unix/vncpasswd/CMakeLists.txt
cfbdf6
@@ -5,5 +5,5 @@ add_executable(vncpasswd
cfbdf6
 
cfbdf6
 target_link_libraries(vncpasswd tx rfb os)
cfbdf6
 
cfbdf6
-install(TARGETS vncpasswd DESTINATION ${BIN_DIR})
cfbdf6
-install(FILES vncpasswd.man DESTINATION ${MAN_DIR}/man1 RENAME vncpasswd.1)
cfbdf6
+install(TARGETS vncpasswd DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
cfbdf6
+install(FILES vncpasswd.man DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1 RENAME vncpasswd.1)
cfbdf6
diff --git a/unix/vncpasswd/vncpasswd.man b/unix/vncpasswd/vncpasswd.man
cfbdf6
index 9e68181..c70a425 100644
cfbdf6
--- a/unix/vncpasswd/vncpasswd.man
cfbdf6
+++ b/unix/vncpasswd/vncpasswd.man
cfbdf6
@@ -43,9 +43,9 @@ Default location of the VNC password file.
cfbdf6
 
cfbdf6
 .SH SEE ALSO
cfbdf6
 .BR vncviewer (1),
cfbdf6
-.BR vncserver (1),
cfbdf6
 .BR Xvnc (1)
cfbdf6
 .BR vncconfig (1),
cfbdf6
+.BR vncsession (8)
cfbdf6
 .br
cfbdf6
 https://www.tigervnc.org
cfbdf6
 
cfbdf6
diff --git a/unix/vncserver.man b/unix/vncserver.man
cfbdf6
deleted file mode 100644
cfbdf6
index 95f7960..0000000
cfbdf6
--- a/unix/vncserver.man
cfbdf6
+++ /dev/null
cfbdf6
@@ -1,204 +0,0 @@
cfbdf6
-.TH vncserver 1 "" "TigerVNC" "Virtual Network Computing"
cfbdf6
-.SH NAME
cfbdf6
-vncserver \- start or stop a VNC server
cfbdf6
-.SH SYNOPSIS
cfbdf6
-.B vncserver
cfbdf6
-.RI [: display# ]
cfbdf6
-.RB [ \-name
cfbdf6
-.IR desktop-name ]
cfbdf6
-.RB [ \-geometry
cfbdf6
-.IR width x height ]
cfbdf6
-.RB [ \-depth
cfbdf6
-.IR depth ]
cfbdf6
-.RB [ \-pixelformat
cfbdf6
-.IR format ]
cfbdf6
-.RB [ \-fp
cfbdf6
-.IR font-path ]
cfbdf6
-.RB [ \-fg ]
cfbdf6
-.RB [ \-autokill ]
cfbdf6
-.RB [ \-noxstartup ]
cfbdf6
-.RB [ \-xstartup 
cfbdf6
-.IR script ]
cfbdf6
-.RI [ Xvnc-options... ]
cfbdf6
-.br
cfbdf6
-.BI "vncserver \-kill :" display#
cfbdf6
-.br
cfbdf6
-.BI "vncserver \-list"
cfbdf6
-.SH DESCRIPTION
cfbdf6
-.B vncserver
cfbdf6
-is used to start a VNC (Virtual Network Computing) desktop.
cfbdf6
-.B vncserver
cfbdf6
-is a Perl script which simplifies the process of starting an Xvnc server.  It
cfbdf6
-runs Xvnc with appropriate options and starts a window manager on the VNC
cfbdf6
-desktop.
cfbdf6
-
cfbdf6
-.B vncserver
cfbdf6
-can be run with no options at all. In this case it will choose the first
cfbdf6
-available display number (usually :1), start Xvnc with that display number,
cfbdf6
-and start the default window manager in the Xvnc session.  You can also
cfbdf6
-specify the display number, in which case vncserver will attempt to start
cfbdf6
-Xvnc with that display number and exit if the display number is not
cfbdf6
-available.  For example:
cfbdf6
-
cfbdf6
-.RS
cfbdf6
-vncserver :13
cfbdf6
-.RE
cfbdf6
-
cfbdf6
-Editing the file $HOME/.vnc/xstartup allows you to change the applications run
cfbdf6
-at startup (but note that this will not affect an existing VNC session.)
cfbdf6
-
cfbdf6
-.SH OPTIONS
cfbdf6
-You can get a list of options by passing \fB\-h\fP as an option to vncserver.
cfbdf6
-In addition to the options listed below, any unrecognised options will be
cfbdf6
-passed to Xvnc - see the Xvnc man page, or "Xvnc \-help", for details.
cfbdf6
-
cfbdf6
-.TP
cfbdf6
-.B \-name \fIdesktop-name\fP
cfbdf6
-Each VNC desktop has a name which may be displayed by the viewer. The desktop
cfbdf6
-name defaults to "\fIhost\fP:\fIdisplay#\fP (\fIusername\fP)", but you can
cfbdf6
-change it with this option.  The desktop name option is passed to the xstartup
cfbdf6
-script via the $VNCDESKTOP environment variable, which allows you to run a
cfbdf6
-different set of applications depending on the name of the desktop.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-geometry \fIwidth\fPx\fIheight\fP
cfbdf6
-Specify the size of the VNC desktop to be created. Default is 1024x768. 
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-depth \fIdepth\fP
cfbdf6
-Specify the pixel depth (in bits) of the VNC desktop to be created. Default is
cfbdf6
-24.  Other possible values are 8, 15 and 16 - anything else is likely to cause
cfbdf6
-strange behaviour by applications.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-pixelformat \fIformat\fP
cfbdf6
-Specify pixel format for Xvnc to use (BGRnnn or RGBnnn).  The default for
cfbdf6
-depth 8 is BGR233 (meaning the most significant two bits represent blue, the
cfbdf6
-next three green, and the least significant three represent red), the default
cfbdf6
-for depth 16 is RGB565, and the default for depth 24 is RGB888.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-cc 3
cfbdf6
-As an alternative to the default TrueColor visual, this allows you to run an
cfbdf6
-Xvnc server with a PseudoColor visual (i.e. one which uses a color map or
cfbdf6
-palette), which can be useful for running some old X applications which only
cfbdf6
-work on such a display.  Values other than 3 (PseudoColor) and 4 (TrueColor)
cfbdf6
-for the \-cc option may result in strange behaviour, and PseudoColor desktops
cfbdf6
-must have an 8-bit depth.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-kill :\fIdisplay#\fP
cfbdf6
-This kills a VNC desktop previously started with vncserver.  It does this by
cfbdf6
-killing the Xvnc process, whose process ID is stored in the file
cfbdf6
-"$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.pid".  The
cfbdf6
-.B \-kill
cfbdf6
-option ignores anything preceding the first colon (":") in the display
cfbdf6
-argument.  Thus, you can invoke "vncserver \-kill $DISPLAY", for example at the
cfbdf6
-end of your xstartup file after a particular application exits.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-fp \fIfont-path\fP
cfbdf6
-If the vncserver script detects that the X Font Server (XFS) is running, it
cfbdf6
-will attempt to start Xvnc and configure Xvnc to use XFS for font handling.
cfbdf6
-Otherwise, if XFS is not running, the vncserver script will attempt to start
cfbdf6
-Xvnc and allow Xvnc to use its own preferred method of font handling (which may
cfbdf6
-be a hard-coded font path or, on more recent systems, a font catalog.)  In
cfbdf6
-any case, if Xvnc fails to start, the vncserver script will then attempt to
cfbdf6
-determine an appropriate X font path for this system and start Xvnc using
cfbdf6
-that font path.
cfbdf6
-
cfbdf6
-The
cfbdf6
-.B \-fp
cfbdf6
-argument allows you to override the above fallback logic and specify a font
cfbdf6
-path for Xvnc to use.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-fg
cfbdf6
-Runs Xvnc as a foreground process.  This has two effects: (1) The VNC server
cfbdf6
-can be aborted with CTRL-C, and (2) the VNC server will exit as soon as the
cfbdf6
-user logs out of the window manager in the VNC session.  This may be necessary
cfbdf6
-when launching TigerVNC from within certain grid computing environments.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-autokill
cfbdf6
-Automatically kill Xvnc whenever the xstartup script exits.  In most cases,
cfbdf6
-this has the effect of terminating Xvnc when the user logs out of the window
cfbdf6
-manager.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-noxstartup
cfbdf6
-Do not run the %HOME/.vnc/xstartup script after launching Xvnc.  This
cfbdf6
-option allows you to manually start a window manager in your TigerVNC session.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-xstartup \fIscript\fP
cfbdf6
-Run a custom startup script, instead of %HOME/.vnc/xstartup, after launching
cfbdf6
-Xvnc. This is useful to run full-screen applications.
cfbdf6
-.
cfbdf6
-.TP
cfbdf6
-.B \-list
cfbdf6
-Lists all VNC desktops started by vncserver.
cfbdf6
-
cfbdf6
-.SH FILES
cfbdf6
-Several VNC-related files are found in the directory $HOME/.vnc:
cfbdf6
-.TP
cfbdf6
-$HOME/.vnc/xstartup
cfbdf6
-A shell script specifying X applications to be run when a VNC desktop is
cfbdf6
-started.  If this file does not exist, then vncserver will create a default
cfbdf6
-xstartup script which attempts to launch your chosen window manager.
cfbdf6
-.TP
cfbdf6
-/etc/tigervnc/vncserver-config-defaults
cfbdf6
-The optional system-wide equivalent of $HOME/.vnc/config. If this file exists
cfbdf6
-and defines options to be passed to Xvnc, they will be used as defaults for
cfbdf6
-users. The user's $HOME/.vnc/config overrides settings configured in this file.
cfbdf6
-The overall configuration file load order is: this file, $HOME/.vnc/config,
cfbdf6
-and then /etc/tigervnc/vncserver-config-mandatory. None are required to exist.
cfbdf6
-.TP
cfbdf6
-/etc/tigervnc/vncserver-config-mandatory
cfbdf6
-The optional system-wide equivalent of $HOME/.vnc/config. If this file exists
cfbdf6
-and defines options to be passed to Xvnc, they will override any of the same
cfbdf6
-options defined in a user's $HOME/.vnc/config. This file offers a mechanism
cfbdf6
-to establish some basic form of system-wide policy. WARNING! There is
cfbdf6
-nothing stopping users from constructing their own vncserver-like script
cfbdf6
-that calls Xvnc directly to bypass any options defined in
cfbdf6
-/etc/tigervnc/vncserver-config-mandatory.  Likewise, any CLI arguments passed
cfbdf6
-to vncserver will override ANY config file setting of the same name. The
cfbdf6
-overall configuration file load order is:
cfbdf6
-/etc/tigervnc/vncserver-config-defaults, $HOME/.vnc/config, and then this file.
cfbdf6
-None are required to exist.
cfbdf6
-.TP
cfbdf6
-$HOME/.vnc/config
cfbdf6
-An optional server config file wherein options to be passed to Xvnc are listed
cfbdf6
-to avoid hard-coding them to the physical invocation. List options in this file
cfbdf6
-one per line. For those requiring an argument, simply separate the option from
cfbdf6
-the argument with an equal sign, for example: "geometry=2000x1200" or
cfbdf6
-"securitytypes=vncauth,tlsvnc". Options without an argument are simply listed
cfbdf6
-as a single word, for example: "localhost" or "alwaysshared".
cfbdf6
-.TP
cfbdf6
-$HOME/.vnc/passwd
cfbdf6
-The VNC password file.
cfbdf6
-.TP
cfbdf6
-$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.log
cfbdf6
-The log file for Xvnc and applications started in xstartup.
cfbdf6
-.TP
cfbdf6
-$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.pid
cfbdf6
-Identifies the Xvnc process ID, used by the
cfbdf6
-.B \-kill
cfbdf6
-option.
cfbdf6
-
cfbdf6
-.SH SEE ALSO
cfbdf6
-.BR vncviewer (1),
cfbdf6
-.BR vncpasswd (1),
cfbdf6
-.BR vncconfig (1),
cfbdf6
-.BR Xvnc (1)
cfbdf6
-.br
cfbdf6
-https://www.tigervnc.org
cfbdf6
-
cfbdf6
-.SH AUTHOR
cfbdf6
-Tristan Richardson, RealVNC Ltd., D. R. Commander and others.
cfbdf6
-
cfbdf6
-VNC was originally developed by the RealVNC team while at Olivetti
cfbdf6
-Research Ltd / AT&T Laboratories Cambridge.  TightVNC additions were
cfbdf6
-implemented by Constantin Kaplinsky. Many other people have since
cfbdf6
-participated in development, testing and support. This manual is part
cfbdf6
-of the TigerVNC software suite.
cfbdf6
diff --git a/unix/vncserver/CMakeLists.txt b/unix/vncserver/CMakeLists.txt
cfbdf6
new file mode 100644
cfbdf6
index 0000000..eeb4b7b
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/CMakeLists.txt
cfbdf6
@@ -0,0 +1,20 @@
cfbdf6
+add_executable(vncsession vncsession.c)
cfbdf6
+target_link_libraries(vncsession ${PAM_LIBS})
cfbdf6
+
cfbdf6
+configure_file(vncserver@.service.in vncserver@.service @ONLY)
cfbdf6
+configure_file(vncsession-start.in vncsession-start @ONLY)
cfbdf6
+configure_file(vncserver.in vncserver @ONLY)
cfbdf6
+
cfbdf6
+install(TARGETS vncsession DESTINATION ${CMAKE_INSTALL_FULL_SBINDIR})
cfbdf6
+install(FILES tigervnc.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME tigervnc)
cfbdf6
+install(FILES vncsession.man DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man8 RENAME vncsession.8)
cfbdf6
+install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vncserver DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR})
cfbdf6
+install(FILES vncserver.man DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man8 RENAME vncserver.8)
cfbdf6
+install(FILES vncserver-config-defaults vncserver-config-mandatory DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/tigervnc)
cfbdf6
+
cfbdf6
+install(FILES vncserver.users DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/tigervnc)
cfbdf6
+
cfbdf6
+if(INSTALL_SYSTEMD_UNITS)
cfbdf6
+  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/vncserver@.service DESTINATION ${CMAKE_INSTALL_FULL_UNITDIR})
cfbdf6
+  install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vncsession-start DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR})
cfbdf6
+endif()
cfbdf6
diff --git a/unix/vncserver/selinux/Makefile b/unix/vncserver/selinux/Makefile
cfbdf6
new file mode 100644
cfbdf6
index 0000000..904a2d5
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/selinux/Makefile
cfbdf6
@@ -0,0 +1,24 @@
cfbdf6
+# SELinux module for TigerVNC's vncsession
cfbdf6
+#
cfbdf6
+# This will install the policy module, but not load it. To apply
cfbdf6
+# it you should also run:
cfbdf6
+#
cfbdf6
+#     sudo semodule -i /usr/share/selinux/packages/vncsession.pp
cfbdf6
+#     sudo restorecon /usr/sbin/vncsession /usr/libexec/vncsession-start
cfbdf6
+#
cfbdf6
+
cfbdf6
+PREFIX=/usr
cfbdf6
+DATADIR=$(PREFIX)/share
cfbdf6
+
cfbdf6
+all: vncsession.pp
cfbdf6
+
cfbdf6
+%.pp: %.te
cfbdf6
+	make -f $(DATADIR)/selinux/devel/Makefile $@
cfbdf6
+
cfbdf6
+clean:
cfbdf6
+	rm -f *.pp
cfbdf6
+	rm -rf tmp
cfbdf6
+
cfbdf6
+install: vncsession.pp
cfbdf6
+	mkdir -p $(DESTDIR)$(DATADIR)/selinux/packages
cfbdf6
+	install vncsession.pp $(DESTDIR)$(DATADIR)/selinux/packages/vncsession.pp 
cfbdf6
diff --git a/unix/vncserver/selinux/vncsession.fc b/unix/vncserver/selinux/vncsession.fc
cfbdf6
new file mode 100644
cfbdf6
index 0000000..121cdd2
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/selinux/vncsession.fc
cfbdf6
@@ -0,0 +1,26 @@
cfbdf6
+#
cfbdf6
+#  Copyright 2018 Pierre Ossman for Cendio AB
cfbdf6
+#
cfbdf6
+#  This is free software; you can redistribute it and/or modify
cfbdf6
+#  it under the terms of the GNU General Public License as published by
cfbdf6
+#  the Free Software Foundation; either version 2 of the License, or
cfbdf6
+#  (at your option) any later version.
cfbdf6
+#
cfbdf6
+#  This software is distributed in the hope that it will be useful,
cfbdf6
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
cfbdf6
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cfbdf6
+#  GNU General Public License for more details.
cfbdf6
+#
cfbdf6
+#  You should have received a copy of the GNU General Public License
cfbdf6
+#  along with this software; if not, write to the Free Software
cfbdf6
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
cfbdf6
+#  USA.
cfbdf6
+#
cfbdf6
+
cfbdf6
+HOME_DIR/\.vnc(/.*)?      gen_context(system_u:object_r:xdm_home_t,s0)
cfbdf6
+HOME_ROOT/\.vnc(/.*)?      gen_context(system_u:object_r:xdm_home_t,s0)
cfbdf6
+
cfbdf6
+/usr/sbin/vncsession			--	gen_context(system_u:object_r:vnc_session_exec_t,s0)
cfbdf6
+/usr/libexec/vncsession-start		--	gen_context(system_u:object_r:vnc_session_exec_t,s0)
cfbdf6
+
cfbdf6
+/var/run/vncsession-:[0-9]*\.pid	--      gen_context(system_u:object_r:vnc_session_var_run_t,s0)
cfbdf6
diff --git a/unix/vncserver/selinux/vncsession.if b/unix/vncserver/selinux/vncsession.if
cfbdf6
new file mode 100644
cfbdf6
index 0000000..3eb6a30
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/selinux/vncsession.if
cfbdf6
@@ -0,0 +1 @@
cfbdf6
+## <summary></summary>
cfbdf6
diff --git a/unix/vncserver/selinux/vncsession.te b/unix/vncserver/selinux/vncsession.te
cfbdf6
new file mode 100644
cfbdf6
index 0000000..941f28d
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/selinux/vncsession.te
cfbdf6
@@ -0,0 +1,67 @@
cfbdf6
+#
cfbdf6
+#  Copyright 2018-2020 Pierre Ossman for Cendio AB
cfbdf6
+#
cfbdf6
+#  This is free software; you can redistribute it and/or modify
cfbdf6
+#  it under the terms of the GNU General Public License as published by
cfbdf6
+#  the Free Software Foundation; either version 2 of the License, or
cfbdf6
+#  (at your option) any later version.
cfbdf6
+#
cfbdf6
+#  This software is distributed in the hope that it will be useful,
cfbdf6
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
cfbdf6
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cfbdf6
+#  GNU General Public License for more details.
cfbdf6
+#
cfbdf6
+#  You should have received a copy of the GNU General Public License
cfbdf6
+#  along with this software; if not, write to the Free Software
cfbdf6
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
cfbdf6
+#  USA.
cfbdf6
+#
cfbdf6
+
cfbdf6
+policy_module(vncsession, 1.0.0);
cfbdf6
+
cfbdf6
+gen_require(`
cfbdf6
+    type unconfined_t;
cfbdf6
+    type xdm_home_t;
cfbdf6
+')
cfbdf6
+
cfbdf6
+type vnc_session_exec_t;
cfbdf6
+corecmd_executable_file(vnc_session_exec_t)
cfbdf6
+type vnc_session_t;
cfbdf6
+init_daemon_domain(vnc_session_t, vnc_session_exec_t)
cfbdf6
+auth_login_pgm_domain(vnc_session_t)
cfbdf6
+
cfbdf6
+type vnc_session_var_run_t;
cfbdf6
+files_pid_file(vnc_session_var_run_t)
cfbdf6
+allow vnc_session_t vnc_session_var_run_t:file manage_file_perms;
cfbdf6
+files_pid_filetrans(vnc_session_t, vnc_session_var_run_t, file)
cfbdf6
+
cfbdf6
+auth_write_login_records(vnc_session_t)
cfbdf6
+
cfbdf6
+can_exec(vnc_session_t, vnc_session_exec_t)
cfbdf6
+
cfbdf6
+userdom_spec_domtrans_all_users(vnc_session_t)
cfbdf6
+userdom_signal_all_users(vnc_session_t)
cfbdf6
+
cfbdf6
+allow vnc_session_t self:capability { kill chown dac_override dac_read_search fowner setgid setuid sys_resource };
cfbdf6
+allow vnc_session_t self:process { getcap setsched setexec setrlimit };
cfbdf6
+allow vnc_session_t self:fifo_file rw_fifo_file_perms;
cfbdf6
+
cfbdf6
+manage_files_pattern(vnc_session_t, xdm_home_t, xdm_home_t)
cfbdf6
+manage_fifo_files_pattern(vnc_session_t, xdm_home_t, xdm_home_t)
cfbdf6
+manage_sock_files_pattern(vnc_session_t, xdm_home_t, xdm_home_t)
cfbdf6
+manage_lnk_files_pattern(vnc_session_t, xdm_home_t, xdm_home_t)
cfbdf6
+
cfbdf6
+userdom_user_home_dir_filetrans(unconfined_t, xdm_home_t, dir, ".vnc")
cfbdf6
+userdom_user_home_dir_filetrans(vnc_session_t, xdm_home_t, dir, ".vnc")
cfbdf6
+
cfbdf6
+userdom_admin_home_dir_filetrans(vnc_session_t, xdm_home_t, dir, ".vnc")
cfbdf6
+userdom_admin_home_dir_filetrans(unconfined_t, xdm_home_t, dir, ".vnc")
cfbdf6
+
cfbdf6
+miscfiles_read_localization(vnc_session_t)
cfbdf6
+
cfbdf6
+kernel_read_kernel_sysctls(vnc_session_t)
cfbdf6
+
cfbdf6
+logging_append_all_logs(vnc_session_t)
cfbdf6
+
cfbdf6
+mcs_process_set_categories(vnc_session_t)
cfbdf6
+mcs_killall(vnc_session_t)
cfbdf6
diff --git a/unix/vncserver/tigervnc.pam b/unix/vncserver/tigervnc.pam
cfbdf6
new file mode 100644
cfbdf6
index 0000000..0f4cb3a
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/tigervnc.pam
cfbdf6
@@ -0,0 +1,11 @@
cfbdf6
+#%PAM-1.0
cfbdf6
+# pam_selinux.so close should be the first session rule
cfbdf6
+-session   required     pam_selinux.so close
cfbdf6
+session    required     pam_loginuid.so
cfbdf6
+-session   required     pam_selinux.so open
cfbdf6
+session    required     pam_namespace.so
cfbdf6
+session    optional     pam_keyinit.so force revoke
cfbdf6
+session    required     pam_limits.so
cfbdf6
+-session   optional     pam_systemd.so
cfbdf6
+session    required     pam_unix.so
cfbdf6
+-session   optional     pam_reauthorize.so prepare
cfbdf6
diff --git a/unix/vncserver/vncserver-config-defaults b/unix/vncserver/vncserver-config-defaults
cfbdf6
new file mode 100644
cfbdf6
index 0000000..0c217bf
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncserver-config-defaults
cfbdf6
@@ -0,0 +1,15 @@
cfbdf6
+## Default settings for VNC servers started by the vncserver service
cfbdf6
+#
cfbdf6
+# Any settings given here will override the builtin defaults, but can
cfbdf6
+# also be overriden by ~/.vnc/config and vncserver-config-mandatory.
cfbdf6
+#
cfbdf6
+# See the following manpages for more details: vncserver(1) Xvnc(1)
cfbdf6
+#
cfbdf6
+# Several common settings are shown below. Uncomment and modify to your
cfbdf6
+# liking.
cfbdf6
+
cfbdf6
+# securitytypes=vncauth,tlsvnc
cfbdf6
+# desktop=sandbox
cfbdf6
+# geometry=2000x1200
cfbdf6
+# localhost
cfbdf6
+# alwaysshared
cfbdf6
diff --git a/unix/vncserver/vncserver-config-mandatory b/unix/vncserver/vncserver-config-mandatory
cfbdf6
new file mode 100644
cfbdf6
index 0000000..98c32f6
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncserver-config-mandatory
cfbdf6
@@ -0,0 +1,15 @@
cfbdf6
+## Mandatory settings for VNC servers started by the vncserver service
cfbdf6
+#
cfbdf6
+# Any settings given here will override the builtin defaults and
cfbdf6
+# settings specified in ~/.vnc/config or vnc-config-defaults.
cfbdf6
+#
cfbdf6
+# See the following manpages for more details: vncserver(1) Xvnc(1)
cfbdf6
+#
cfbdf6
+# Several common settings are shown below. Uncomment and modify to your
cfbdf6
+# liking.
cfbdf6
+
cfbdf6
+# securitytypes=vncauth,tlsvnc
cfbdf6
+# desktop=sandbox
cfbdf6
+# geometry=2000x1200
cfbdf6
+# localhost
cfbdf6
+# alwaysshared
cfbdf6
diff --git a/unix/vncserver/vncserver.in b/unix/vncserver/vncserver.in
cfbdf6
new file mode 100755
cfbdf6
index 0000000..8e05b72
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncserver.in
cfbdf6
@@ -0,0 +1,485 @@
cfbdf6
+#!/usr/bin/perl
cfbdf6
+#
cfbdf6
+#  Copyright (C) 2015-2019 Pierre Ossman for Cendio AB
cfbdf6
+#  Copyright (C) 2009-2010 D. R. Commander.  All Rights Reserved.
cfbdf6
+#  Copyright (C) 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
cfbdf6
+#  Copyright (C) 2002-2003 Constantin Kaplinsky.  All Rights Reserved.
cfbdf6
+#  Copyright (C) 2002-2005 RealVNC Ltd.
cfbdf6
+#  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
cfbdf6
+#
cfbdf6
+#  This is free software; you can redistribute it and/or modify
cfbdf6
+#  it under the terms of the GNU General Public License as published by
cfbdf6
+#  the Free Software Foundation; either version 2 of the License, or
cfbdf6
+#  (at your option) any later version.
cfbdf6
+#
cfbdf6
+#  This software is distributed in the hope that it will be useful,
cfbdf6
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
cfbdf6
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cfbdf6
+#  GNU General Public License for more details.
cfbdf6
+#
cfbdf6
+#  You should have received a copy of the GNU General Public License
cfbdf6
+#  along with this software; if not, write to the Free Software
cfbdf6
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
cfbdf6
+#  USA.
cfbdf6
+#
cfbdf6
+
cfbdf6
+#
cfbdf6
+# vncserver - wrapper script to start an X VNC server.
cfbdf6
+#
cfbdf6
+
cfbdf6
+&SanityCheck();
cfbdf6
+
cfbdf6
+#
cfbdf6
+# Global variables.  You may want to configure some of these for
cfbdf6
+# your site
cfbdf6
+#
cfbdf6
+
cfbdf6
+$vncUserDir = "$ENV{HOME}/.vnc";
cfbdf6
+$vncUserConfig = "$vncUserDir/config";
cfbdf6
+
cfbdf6
+$vncSystemConfigDir = "/etc/tigervnc";
cfbdf6
+$vncSystemConfigDefaultsFile = "$vncSystemConfigDir/vncserver-config-defaults";
cfbdf6
+$vncSystemConfigMandatoryFile = "$vncSystemConfigDir/vncserver-config-mandatory";
cfbdf6
+
cfbdf6
+$xauthorityFile = "$ENV{XAUTHORITY}" || "$ENV{HOME}/.Xauthority";
cfbdf6
+
cfbdf6
+chop($host = `uname -n`);
cfbdf6
+
cfbdf6
+if (-d "/etc/X11/fontpath.d") {
cfbdf6
+    $fontPath = "catalogue:/etc/X11/fontpath.d";
cfbdf6
+}
cfbdf6
+
cfbdf6
+@fontpaths = ('/usr/share/X11/fonts', '/usr/share/fonts', '/usr/share/fonts/X11/');
cfbdf6
+if (! -l "/usr/lib/X11") {push(@fontpaths, '/usr/lib/X11/fonts');}
cfbdf6
+if (! -l "/usr/X11") {push(@fontpaths, '/usr/X11/lib/X11/fonts');}
cfbdf6
+if (! -l "/usr/X11R6") {push(@fontpaths, '/usr/X11R6/lib/X11/fonts');}
cfbdf6
+push(@fontpaths, '/usr/share/fonts/default');
cfbdf6
+
cfbdf6
+@fonttypes = ('misc',
cfbdf6
+             '75dpi',
cfbdf6
+             '100dpi',
cfbdf6
+             'Speedo',
cfbdf6
+             'Type1');
cfbdf6
+
cfbdf6
+foreach $_fpath (@fontpaths) {
cfbdf6
+    foreach $_ftype (@fonttypes) {
cfbdf6
+        if (-f "$_fpath/$_ftype/fonts.dir") {
cfbdf6
+            if (! -l "$_fpath/$_ftype") {
cfbdf6
+                $defFontPath .= "$_fpath/$_ftype,";
cfbdf6
+            }
cfbdf6
+        }
cfbdf6
+    }
cfbdf6
+}
cfbdf6
+
cfbdf6
+if ($defFontPath) {
cfbdf6
+    if (substr($defFontPath, -1, 1) == ',') {
cfbdf6
+        chop $defFontPath;
cfbdf6
+    }
cfbdf6
+}
cfbdf6
+
cfbdf6
+if ($fontPath eq "") {
cfbdf6
+    $fontPath = $defFontPath;
cfbdf6
+}
cfbdf6
+
cfbdf6
+# Find display number.
cfbdf6
+if ((@ARGV == 1) && ($ARGV[0] =~ /^:(\d+)$/)) {
cfbdf6
+    $displayNumber = $1;
cfbdf6
+    if (!&CheckDisplayNumber($displayNumber)) {
cfbdf6
+	die "A VNC server is already running as :$displayNumber\n";
cfbdf6
+    }
cfbdf6
+} else {
cfbdf6
+    &Usage();
cfbdf6
+}
cfbdf6
+
cfbdf6
+$vncPort = 5900 + $displayNumber;
cfbdf6
+
cfbdf6
+$desktopName = "$host:$displayNumber ($ENV{USER})";
cfbdf6
+
cfbdf6
+my %default_opts;
cfbdf6
+my %config;
cfbdf6
+
cfbdf6
+# We set some reasonable defaults. Config file settings
cfbdf6
+# override these where present.
cfbdf6
+$default_opts{desktop} = $desktopName;
cfbdf6
+$default_opts{auth} = $xauthorityFile;
cfbdf6
+$default_opts{rfbwait} = 30000;
cfbdf6
+$default_opts{rfbauth} = "$vncUserDir/passwd";
cfbdf6
+$default_opts{rfbport} = $vncPort;
cfbdf6
+$default_opts{fp} = $fontPath if ($fontPath);
cfbdf6
+$default_opts{pn} = "";
cfbdf6
+
cfbdf6
+# Load user-overrideable system defaults
cfbdf6
+LoadConfig($vncSystemConfigDefaultsFile);
cfbdf6
+
cfbdf6
+# Then the user's settings
cfbdf6
+LoadConfig($vncUserConfig);
cfbdf6
+
cfbdf6
+# And then override anything set above if mandatory settings exist.
cfbdf6
+# WARNING: "Mandatory" is used loosely here! As the man page says,
cfbdf6
+# there is nothing stopping someone from EASILY subverting the
cfbdf6
+# settings in $vncSystemConfigMandatoryFile by simply passing
cfbdf6
+# CLI args to vncserver, which trump config files! To properly
cfbdf6
+# hard force policy in a non-subvertible way would require major
cfbdf6
+# development work that touches Xvnc itself.
cfbdf6
+LoadConfig($vncSystemConfigMandatoryFile, 1);
cfbdf6
+
cfbdf6
+#
cfbdf6
+# Check whether VNC authentication is enabled, and if so, check that
cfbdf6
+# a VNC password has been created.
cfbdf6
+#
cfbdf6
+
cfbdf6
+$securityTypeArgSpecified = 0;
cfbdf6
+$vncAuthEnabled = 0;
cfbdf6
+$passwordArgSpecified = 0;
cfbdf6
+@vncAuthStrings = ("vncauth", "tlsvnc", "x509vnc");
cfbdf6
+
cfbdf6
+# ...first we check our configuration files' settings
cfbdf6
+if ($config{'securitytypes'}) {
cfbdf6
+  $securityTypeArgSpecified = 1;
cfbdf6
+  foreach $arg2 (split(',', $config{'securitytypes'})) {
cfbdf6
+    if (grep {$_ eq lc($arg2)} @vncAuthStrings) {
cfbdf6
+      $vncAuthEnabled = 1;
cfbdf6
+    }
cfbdf6
+  }
cfbdf6
+}
cfbdf6
+
cfbdf6
+if ($config{'password'} ||
cfbdf6
+    $config{'passwordfile'} ||
cfbdf6
+    $config{'rfbauth'}) {
cfbdf6
+    $passwordArgSpecified = 1;
cfbdf6
+}
cfbdf6
+
cfbdf6
+if ((!$securityTypeArgSpecified || $vncAuthEnabled) && !$passwordArgSpecified) {
cfbdf6
+    ($z,$z,$mode) = stat("$vncUserDir/passwd");
cfbdf6
+    if (!(-e "$vncUserDir/passwd") || ($mode & 077)) {
cfbdf6
+        die "VNC authentication enabled, but no password file created.\n";
cfbdf6
+    }
cfbdf6
+}
cfbdf6
+
cfbdf6
+#
cfbdf6
+# Find a desktop session to run
cfbdf6
+#
cfbdf6
+
cfbdf6
+my $sessionname;
cfbdf6
+my %session;
cfbdf6
+
cfbdf6
+$sessionname = delete $config{'session'};
cfbdf6
+
cfbdf6
+if ($sessionname) {
cfbdf6
+  %session = LoadXSession($sessionname);
cfbdf6
+  if (!%session) {
cfbdf6
+    warn "Could not load configured desktop session $sessionname\n";
cfbdf6
+    $sessionname = undef;
cfbdf6
+  }
cfbdf6
+}
cfbdf6
+
cfbdf6
+if (!$sessionname) {
cfbdf6
+  foreach $file (glob("/usr/share/xsessions/*.desktop")) {
cfbdf6
+    ($name) = $file =~ /^.*\/(.*)[.]desktop$/;
cfbdf6
+    %session = LoadXSession($name);
cfbdf6
+    if (%session) {
cfbdf6
+      $sessionname = $name;
cfbdf6
+      last;
cfbdf6
+    }
cfbdf6
+  }
cfbdf6
+}
cfbdf6
+
cfbdf6
+if (!$sessionname) {
cfbdf6
+  die "Could not find a desktop session to run\n";
cfbdf6
+}
cfbdf6
+
cfbdf6
+warn "Using desktop session $sessionname\n";
cfbdf6
+
cfbdf6
+if (!$session{'Exec'}) {
cfbdf6
+  die "No command specified for desktop session\n";
cfbdf6
+}
cfbdf6
+
cfbdf6
+$ENV{GDMSESSION} = $sessionname;
cfbdf6
+$ENV{DESKTOP_SESSION} = $sessionname;
cfbdf6
+$ENV{XDG_SESSION_DESKTOP} = $sessionname;
cfbdf6
+
cfbdf6
+if ($session{'DesktopNames'}) {
cfbdf6
+    $ENV{XDG_DESKTOP_NAMES} = $session{'DesktopNames'} =~ s/;/:/gr;
cfbdf6
+}
cfbdf6
+
cfbdf6
+
cfbdf6
+# Make an X server cookie and set up the Xauthority file
cfbdf6
+# mcookie is a part of util-linux, usually only GNU/Linux systems have it.
cfbdf6
+$cookie = `mcookie`;
cfbdf6
+# Fallback for non GNU/Linux OS - use /dev/urandom on systems that have it,
cfbdf6
+# otherwise use perl's random number generator, seeded with the sum
cfbdf6
+# of the current time, our PID and part of the encrypted form of the password.
cfbdf6
+if ($cookie eq "" && open(URANDOM, '<', '/dev/urandom')) {
cfbdf6
+  my $randata;
cfbdf6
+  if (sysread(URANDOM, $randata, 16) == 16) {
cfbdf6
+    $cookie = unpack 'h*', $randata;
cfbdf6
+  }
cfbdf6
+  close(URANDOM);
cfbdf6
+}
cfbdf6
+if ($cookie eq "") {
cfbdf6
+  srand(time+$$+unpack("L",`cat $vncUserDir/passwd`));
cfbdf6
+  for (1..16) {
cfbdf6
+    $cookie .= sprintf("%02x", int(rand(256)) % 256);
cfbdf6
+  }
cfbdf6
+}
cfbdf6
+
cfbdf6
+open(XAUTH, "|xauth -f $xauthorityFile source -");
cfbdf6
+print XAUTH "add $host:$displayNumber . $cookie\n";
cfbdf6
+print XAUTH "add $host/unix:$displayNumber . $cookie\n";
cfbdf6
+close(XAUTH);
cfbdf6
+
cfbdf6
+$ENV{XAUTHORITY} = $xauthorityFile;
cfbdf6
+
cfbdf6
+# Now start the X VNC Server
cfbdf6
+
cfbdf6
+@cmd = ("xinit");
cfbdf6
+
cfbdf6
+push(@cmd, $Xsession, $session{'Exec'});
cfbdf6
+
cfbdf6
+push(@cmd, '--');
cfbdf6
+
cfbdf6
+
cfbdf6
+# We build up our Xvnc command with options
cfbdf6
+push(@cmd, "@CMAKE_INSTALL_FULL_BINDIR@/Xvnc", ":$displayNumber");
cfbdf6
+
cfbdf6
+foreach my $k (sort keys %config) {
cfbdf6
+  push(@cmd, "-$k");
cfbdf6
+  push(@cmd, $config{$k}) if $config{$k};
cfbdf6
+  delete $default_opts{$k}; # file options take precedence
cfbdf6
+}
cfbdf6
+
cfbdf6
+foreach my $k (sort keys %default_opts) {
cfbdf6
+  push(@cmd, "-$k");
cfbdf6
+  push(@cmd, $default_opts{$k}) if $default_opts{$k};
cfbdf6
+}
cfbdf6
+
cfbdf6
+warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
cfbdf6
+
cfbdf6
+warn "Starting desktop session $sessionname\n";
cfbdf6
+
cfbdf6
+exec(@cmd);
cfbdf6
+
cfbdf6
+die "Failed to start session.\n";
cfbdf6
+
cfbdf6
+###############################################################################
cfbdf6
+# Functions
cfbdf6
+###############################################################################
cfbdf6
+
cfbdf6
+#
cfbdf6
+# Populate the global %config hash with settings from a specified
cfbdf6
+# vncserver configuration file if it exists
cfbdf6
+#
cfbdf6
+# Args: 1. file path
cfbdf6
+#       2. optional boolean flag to enable warning when a previously
cfbdf6
+#          set configuration setting is being overridden
cfbdf6
+#
cfbdf6
+sub LoadConfig {
cfbdf6
+  local ($configFile, $warnoverride) = @_;
cfbdf6
+  local ($toggle) = undef;
cfbdf6
+
cfbdf6
+  if (stat($configFile)) {
cfbdf6
+    if (open(IN, $configFile)) {
cfbdf6
+      while (<IN>) {
cfbdf6
+        next if /^#/;
cfbdf6
+        if (my ($k, $v) = /^\s*(\w+)\s*=\s*(.+)$/) {
cfbdf6
+          $k = lc($k); # must normalize key case
cfbdf6
+          if ($warnoverride && $config{$k}) {
cfbdf6
+            print("Warning: $configFile is overriding previously defined '$k' to be '$v'\n");
cfbdf6
+          }
cfbdf6
+          $config{$k} = $v;
cfbdf6
+        } elsif ($_ =~ m/^\s*(\S+)/) {
cfbdf6
+          # We can't reasonably warn on override of toggles (e.g. AlwaysShared)
cfbdf6
+          # because it would get crazy to do so. We'd have to check if the
cfbdf6
+          # current config file being loaded defined the logical opposite setting
cfbdf6
+          # (NeverShared vs. AlwaysShared, etc etc).
cfbdf6
+          $toggle = lc($1); # must normalize key case
cfbdf6
+          $config{$toggle} = $k;
cfbdf6
+        }
cfbdf6
+      }
cfbdf6
+      close(IN);
cfbdf6
+    }
cfbdf6
+  }
cfbdf6
+}
cfbdf6
+
cfbdf6
+
cfbdf6
+#
cfbdf6
+# Load a session desktop file
cfbdf6
+#
cfbdf6
+sub LoadXSession {
cfbdf6
+  local ($name) = @_;
cfbdf6
+  my $file, $found_group, %session;
cfbdf6
+
cfbdf6
+  $file = "/usr/share/xsessions/$name.desktop";
cfbdf6
+
cfbdf6
+  if (!stat($file)) {
cfbdf6
+    warn "Could not find session desktop file $file";
cfbdf6
+    return;
cfbdf6
+  }
cfbdf6
+
cfbdf6
+  if (!open(IN, $file)) {
cfbdf6
+    warn "Could not open session desktop file $file";
cfbdf6
+    return;
cfbdf6
+  }
cfbdf6
+
cfbdf6
+  $found_group = 0;
cfbdf6
+  while (my $line = <IN>) {
cfbdf6
+    next if $line =~ /^#/;
cfbdf6
+    next if $line =~ /^\s*$/;
cfbdf6
+
cfbdf6
+    if (!$found_group) {
cfbdf6
+        next if $line != "[Desktop Entry]";
cfbdf6
+        $found_group = 1;
cfbdf6
+        next;
cfbdf6
+    } else {
cfbdf6
+        last if $line =~ /^\[/;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    my ($key, $value) = $line =~ /^\s*([]A-Za-z0-9_@\-\[]+)\s*=\s*(.*)$/;
cfbdf6
+    if (!$key) {
cfbdf6
+        warn "Invalid session desktop file $file";
cfbdf6
+        close(IN);
cfbdf6
+        return;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    $value =~ s/\\s/ /g;
cfbdf6
+    $value =~ s/\\n/\n/g;
cfbdf6
+    $value =~ s/\\t/\t/g;
cfbdf6
+    $value =~ s/\\r/\r/g;
cfbdf6
+    $value =~ s/\\\\/\\/g;
cfbdf6
+
cfbdf6
+    $session{$key} = $value;
cfbdf6
+  }
cfbdf6
+
cfbdf6
+  close(IN);
cfbdf6
+
cfbdf6
+  return %session;
cfbdf6
+}
cfbdf6
+
cfbdf6
+
cfbdf6
+#
cfbdf6
+# CheckDisplayNumber checks if the given display number is available.  A
cfbdf6
+# display number n is taken if something is listening on the VNC server port
cfbdf6
+# (5900+n) or the X server port (6000+n).
cfbdf6
+#
cfbdf6
+
cfbdf6
+sub CheckDisplayNumber
cfbdf6
+{
cfbdf6
+    local ($n) = @_;
cfbdf6
+
cfbdf6
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
cfbdf6
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
cfbdf6
+    if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
cfbdf6
+	close(S);
cfbdf6
+	return 0;
cfbdf6
+    }
cfbdf6
+    close(S);
cfbdf6
+
cfbdf6
+    socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
cfbdf6
+    eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
cfbdf6
+    if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
cfbdf6
+	close(S);
cfbdf6
+	return 0;
cfbdf6
+    }
cfbdf6
+    close(S);
cfbdf6
+
cfbdf6
+    if (-e "/tmp/.X$n-lock") {
cfbdf6
+	warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
cfbdf6
+	warn "Remove this file if there is no X server $host:$n\n";
cfbdf6
+	return 0;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (-e "/tmp/.X11-unix/X$n") {
cfbdf6
+	warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
cfbdf6
+	warn "Remove this file if there is no X server $host:$n\n";
cfbdf6
+	return 0;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (-e "/usr/spool/sockets/X11/$n") {
cfbdf6
+	warn("\nWarning: $host:$n is taken because of ".
cfbdf6
+             "/usr/spool/sockets/X11/$n\n");
cfbdf6
+	warn "Remove this file if there is no X server $host:$n\n";
cfbdf6
+	return 0;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    return 1;
cfbdf6
+}
cfbdf6
+
cfbdf6
+#
cfbdf6
+# Usage
cfbdf6
+#
cfbdf6
+
cfbdf6
+sub Usage
cfbdf6
+{
cfbdf6
+    die("\nusage: $prog <display>\n\n");
cfbdf6
+}
cfbdf6
+
cfbdf6
+
cfbdf6
+# Routine to make sure we're operating in a sane environment.
cfbdf6
+sub SanityCheck
cfbdf6
+{
cfbdf6
+    local ($cmd);
cfbdf6
+
cfbdf6
+    # Get the program name
cfbdf6
+    ($prog) = ($0 =~ m|([^/]+)$|);
cfbdf6
+
cfbdf6
+    #
cfbdf6
+    # Check we have all the commands we'll need on the path.
cfbdf6
+    #
cfbdf6
+
cfbdf6
+ cmd:
cfbdf6
+    foreach $cmd ("uname","xauth","xinit") {
cfbdf6
+	for (split(/:/,$ENV{PATH})) {
cfbdf6
+	    if (-x "$_/$cmd") {
cfbdf6
+		next cmd;
cfbdf6
+	    }
cfbdf6
+	}
cfbdf6
+	die "$prog: couldn't find \"$cmd\" on your PATH.\n";
cfbdf6
+    }
cfbdf6
+    
cfbdf6
+    foreach $cmd ("/etc/X11/xinit/Xsession", "/etc/X11/Xsession") {
cfbdf6
+        if (-x "$cmd") {
cfbdf6
+            $Xsession = $cmd;
cfbdf6
+            last;
cfbdf6
+        }
cfbdf6
+    }
cfbdf6
+    if (not defined $Xsession) {
cfbdf6
+        die "$prog: Couldn't find suitable Xsession.\n";
cfbdf6
+    }
cfbdf6
+
cfbdf6
+
cfbdf6
+    if (!defined($ENV{HOME})) {
cfbdf6
+	die "$prog: The HOME environment variable is not set.\n";
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    #
cfbdf6
+    # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
cfbdf6
+    # eval, and if it fails we try 'require "sys/socket.ph"'.  If this fails,
cfbdf6
+    # we just guess at the values.  If you find perl moaning here, just
cfbdf6
+    # hard-code the values of AF_INET and SOCK_STREAM.  You can find these out
cfbdf6
+    # for your platform by looking in /usr/include/sys/socket.h and related
cfbdf6
+    # files.
cfbdf6
+    #
cfbdf6
+
cfbdf6
+    chop($os = `uname`);
cfbdf6
+    chop($osrev = `uname -r`);
cfbdf6
+
cfbdf6
+    eval 'use Socket';
cfbdf6
+    if ($@) {
cfbdf6
+	eval 'require "sys/socket.ph"';
cfbdf6
+	if ($@) {
cfbdf6
+	    if (($os eq "SunOS") && ($osrev !~ /^4/)) {
cfbdf6
+		$AF_INET = 2;
cfbdf6
+		$SOCK_STREAM = 2;
cfbdf6
+	    } else {
cfbdf6
+		$AF_INET = 2;
cfbdf6
+		$SOCK_STREAM = 1;
cfbdf6
+	    }
cfbdf6
+	} else {
cfbdf6
+	    $AF_INET = &AF_INET;
cfbdf6
+	    $SOCK_STREAM = &SOCK_STREAM;
cfbdf6
+	}
cfbdf6
+    } else {
cfbdf6
+	$AF_INET = &AF_INET;
cfbdf6
+	$SOCK_STREAM = &SOCK_STREAM;
cfbdf6
+    }
cfbdf6
+}
cfbdf6
diff --git a/unix/vncserver/vncserver.man b/unix/vncserver/vncserver.man
cfbdf6
new file mode 100644
cfbdf6
index 0000000..f1017be
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncserver.man
cfbdf6
@@ -0,0 +1 @@
cfbdf6
+.so man8/vncsession.8
cfbdf6
diff --git a/unix/vncserver/vncserver.users b/unix/vncserver/vncserver.users
cfbdf6
new file mode 100644
cfbdf6
index 0000000..24875c8
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncserver.users
cfbdf6
@@ -0,0 +1,8 @@
cfbdf6
+# TigerVNC User assignment
cfbdf6
+#
cfbdf6
+# This file assigns users to specific VNC display numbers.
cfbdf6
+# The syntax is <display>=<username>. E.g.:
cfbdf6
+#
cfbdf6
+# :2=andrew
cfbdf6
+# :3=lisa
cfbdf6
+ 
cfbdf6
diff --git a/unix/vncserver/vncserver@.service.in b/unix/vncserver/vncserver@.service.in
cfbdf6
new file mode 100644
cfbdf6
index 0000000..584ecf4
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncserver@.service.in
cfbdf6
@@ -0,0 +1,43 @@
cfbdf6
+# The vncserver service unit file
cfbdf6
+#
cfbdf6
+# Quick HowTo:
cfbdf6
+# 1. Add a user mapping to /etc/tigervnc/vncserver.users.
cfbdf6
+# 2. Adjust the global or user configuration. See the
cfbdf6
+#    vncsession(8) manpage for details. (OPTIONAL)
cfbdf6
+# 3. Run `systemctl enable vncserver@:<display>.service`
cfbdf6
+# 4. Run `systemctl start vncserver@:<display>.service`
cfbdf6
+#
cfbdf6
+# DO NOT RUN THIS SERVICE if your local area network is
cfbdf6
+# untrusted!  For a secure way of using VNC, you should
cfbdf6
+# limit connections to the local host and then tunnel from
cfbdf6
+# the machine you want to view VNC on (host A) to the machine
cfbdf6
+# whose VNC output you want to view (host B)
cfbdf6
+#
cfbdf6
+# [user@hostA ~]$ ssh -v -C -L 590N:localhost:590M hostB
cfbdf6
+#
cfbdf6
+# this will open a connection on port 590N of your hostA to hostB's port 590M
cfbdf6
+# (in fact, it ssh-connects to hostB and then connects to localhost (on hostB).
cfbdf6
+# See the ssh man page for details on port forwarding)
cfbdf6
+#
cfbdf6
+# You can then point a VNC client on hostA at vncdisplay N of localhost and with
cfbdf6
+# the help of ssh, you end up seeing what hostB makes available on port 590M
cfbdf6
+#
cfbdf6
+# Use "nolisten=tcp" to prevent X connections to your VNC server via TCP.
cfbdf6
+#
cfbdf6
+# Use "localhost" to prevent remote VNC clients connecting except when
cfbdf6
+# doing so through a secure tunnel.  See the "-via" option in the
cfbdf6
+# `man vncviewer' manual page.
cfbdf6
+
cfbdf6
+
cfbdf6
+[Unit]
cfbdf6
+Description=Remote desktop service (VNC)
cfbdf6
+After=syslog.target network.target
cfbdf6
+
cfbdf6
+[Service]
cfbdf6
+Type=forking
cfbdf6
+ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR@/vncsession-start %i
cfbdf6
+PIDFile=/var/run/vncsession-%i.pid
cfbdf6
+SELinuxContext=system_u:system_r:vnc_session_t:s0
cfbdf6
+
cfbdf6
+[Install]
cfbdf6
+WantedBy=multi-user.target
cfbdf6
diff --git a/unix/vncserver/vncsession-start.in b/unix/vncserver/vncsession-start.in
cfbdf6
new file mode 100644
cfbdf6
index 0000000..b20fcdd
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncsession-start.in
cfbdf6
@@ -0,0 +1,43 @@
cfbdf6
+#!/bin/bash
cfbdf6
+#
cfbdf6
+#  Copyright 2019 Pierre Ossman for Cendio AB
cfbdf6
+#
cfbdf6
+#  This is free software; you can redistribute it and/or modify
cfbdf6
+#  it under the terms of the GNU General Public License as published by
cfbdf6
+#  the Free Software Foundation; either version 2 of the License, or
cfbdf6
+#  (at your option) any later version.
cfbdf6
+#
cfbdf6
+#  This software is distributed in the hope that it will be useful,
cfbdf6
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
cfbdf6
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cfbdf6
+#  GNU General Public License for more details.
cfbdf6
+#
cfbdf6
+#  You should have received a copy of the GNU General Public License
cfbdf6
+#  along with this software; if not, write to the Free Software
cfbdf6
+#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
cfbdf6
+#  USA.
cfbdf6
+#
cfbdf6
+
cfbdf6
+USERSFILE="@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc/vncserver.users"
cfbdf6
+
cfbdf6
+if [ $# -ne 1 ]; then
cfbdf6
+	echo "Syntax:" >&2
cfbdf6
+	echo "    $0 <display>" >&2
cfbdf6
+	exit 1
cfbdf6
+fi
cfbdf6
+
cfbdf6
+if [ ! -f "${USERSFILE}" ]; then
cfbdf6
+	echo "Users file ${USERSFILE} missing" >&2
cfbdf6
+	exit 1
cfbdf6
+fi
cfbdf6
+
cfbdf6
+DISPLAY="$1"
cfbdf6
+
cfbdf6
+USER=`grep "^${DISPLAY}=" "${USERSFILE}" 2>/dev/null | head -1 | cut -d = -f 2-`
cfbdf6
+
cfbdf6
+if [ -z "${USER}" ]; then
cfbdf6
+	echo "No user configured for display ${DISPLAY}" >&2
cfbdf6
+	exit 1
cfbdf6
+fi
cfbdf6
+
cfbdf6
+exec "@CMAKE_INSTALL_FULL_SBINDIR@/vncsession" "${USER}" "${DISPLAY}"
cfbdf6
diff --git a/unix/vncserver/vncsession.c b/unix/vncserver/vncsession.c
cfbdf6
new file mode 100644
cfbdf6
index 0000000..21a40f6
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncsession.c
cfbdf6
@@ -0,0 +1,592 @@
cfbdf6
+/* 
cfbdf6
+ * Copyright 2018 Pierre Ossman for Cendio AB
cfbdf6
+ *    
cfbdf6
+ * This is free software; you can redistribute it and/or modify
cfbdf6
+ * it under the terms of the GNU General Public License as published by
cfbdf6
+ * the Free Software Foundation; either version 2 of the License, or
cfbdf6
+ * (at your option) any later version.
cfbdf6
+ * 
cfbdf6
+ * This software is distributed in the hope that it will be useful,
cfbdf6
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
cfbdf6
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cfbdf6
+ * GNU General Public License for more details.
cfbdf6
+ * 
cfbdf6
+ * You should have received a copy of the GNU General Public License
cfbdf6
+ * along with this software; if not, write to the Free Software
cfbdf6
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
cfbdf6
+ * USA.
cfbdf6
+ */
cfbdf6
+
cfbdf6
+#include <config.h>
cfbdf6
+
cfbdf6
+#include <dirent.h>
cfbdf6
+#include <errno.h>
cfbdf6
+#include <fcntl.h>
cfbdf6
+#include <grp.h>
cfbdf6
+#include <limits.h>
cfbdf6
+#include <pwd.h>
cfbdf6
+#include <signal.h>
cfbdf6
+#include <stdio.h>
cfbdf6
+#include <stdlib.h>
cfbdf6
+#include <string.h>
cfbdf6
+#include <sysexits.h>
cfbdf6
+#include <unistd.h>
cfbdf6
+#include <syslog.h>
cfbdf6
+#include <security/pam_appl.h>
cfbdf6
+#include <sys/stat.h>
cfbdf6
+#include <sys/types.h>
cfbdf6
+#include <sys/wait.h>
cfbdf6
+
cfbdf6
+extern char **environ;
cfbdf6
+
cfbdf6
+// PAM service name
cfbdf6
+const char *SERVICE_NAME = "tigervnc";
cfbdf6
+
cfbdf6
+// Main script PID
cfbdf6
+volatile static pid_t script = -1;
cfbdf6
+
cfbdf6
+// Daemon completion pipe
cfbdf6
+int daemon_pipe_fd = -1;
cfbdf6
+
cfbdf6
+static int
cfbdf6
+begin_daemon(void)
cfbdf6
+{
cfbdf6
+    int devnull, fds[2];
cfbdf6
+    pid_t pid;
cfbdf6
+
cfbdf6
+    /* Pipe to report startup success */
cfbdf6
+    if (pipe(fds) < 0) {
cfbdf6
+        perror("pipe");
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    /* First fork */
cfbdf6
+    pid = fork();
cfbdf6
+    if (pid < 0) {
cfbdf6
+        perror("fork");
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (pid != 0) {
cfbdf6
+        ssize_t len;
cfbdf6
+        char buf[1];
cfbdf6
+
cfbdf6
+        close(fds[1]);
cfbdf6
+
cfbdf6
+        /* Wait for child to finish startup */
cfbdf6
+        len = read(fds[0], buf, 1);
cfbdf6
+        if (len != 1) {
cfbdf6
+            fprintf(stderr, "Failure daemonizing\n");
cfbdf6
+            _exit(EX_OSERR);
cfbdf6
+        }
cfbdf6
+
cfbdf6
+        _exit(0);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    close(fds[0]);
cfbdf6
+    daemon_pipe_fd = fds[1];
cfbdf6
+
cfbdf6
+    /* Detach from terminal */
cfbdf6
+    if (setsid() < 0) {
cfbdf6
+        perror("setsid");
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    /* Another fork required to fully detach */
cfbdf6
+    pid = fork();
cfbdf6
+    if (pid < 0) {
cfbdf6
+        perror("fork");
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (pid == 0)
cfbdf6
+        _exit(0);
cfbdf6
+
cfbdf6
+    /* Send all stdio to /dev/null */
cfbdf6
+    devnull = open("/dev/null", O_RDWR);
cfbdf6
+    if (devnull < 0) {
cfbdf6
+        fprintf(stderr, "Failed to open /dev/null: %s\n", strerror(errno));
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+    if ((dup2(devnull, 0) < 0) ||
cfbdf6
+        (dup2(devnull, 1) < 0) ||
cfbdf6
+        (dup2(devnull, 2) < 0)) {
cfbdf6
+        perror("dup2");
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+    if (devnull > 2)
cfbdf6
+        close(devnull);
cfbdf6
+
cfbdf6
+    /* Full control of access bits */
cfbdf6
+    umask(0);
cfbdf6
+
cfbdf6
+    /* A safe working directory */
cfbdf6
+    if (chdir("/") < 0) {
cfbdf6
+        perror("chdir");
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    return 0;
cfbdf6
+}
cfbdf6
+
cfbdf6
+static void
cfbdf6
+finish_daemon(void)
cfbdf6
+{
cfbdf6
+    write(daemon_pipe_fd, "+", 1);
cfbdf6
+    close(daemon_pipe_fd);
cfbdf6
+    daemon_pipe_fd = -1;
cfbdf6
+}
cfbdf6
+
cfbdf6
+static void
cfbdf6
+sighandler(int sig)
cfbdf6
+{
cfbdf6
+    if (script > 0) {
cfbdf6
+        kill(script, SIGTERM);
cfbdf6
+    }
cfbdf6
+}
cfbdf6
+
cfbdf6
+static void
cfbdf6
+setup_signals(void)
cfbdf6
+{
cfbdf6
+    struct sigaction act;
cfbdf6
+
cfbdf6
+    memset(&act, 0, sizeof(act));
cfbdf6
+    act.sa_handler = sighandler;
cfbdf6
+    sigemptyset(&act.sa_mask);
cfbdf6
+    act.sa_flags = 0;
cfbdf6
+
cfbdf6
+    sigaction(SIGHUP, &act, NULL);
cfbdf6
+    sigaction(SIGINT, &act, NULL);
cfbdf6
+    sigaction(SIGTERM, &act, NULL);
cfbdf6
+    sigaction(SIGQUIT, &act, NULL);
cfbdf6
+    sigaction(SIGPIPE, &act, NULL);
cfbdf6
+}
cfbdf6
+
cfbdf6
+static int
cfbdf6
+conv(int num_msg,
cfbdf6
+     const struct pam_message **msg,
cfbdf6
+     struct pam_response **resp, void *appdata_ptr)
cfbdf6
+{
cfbdf6
+    /* Opening a session should not require a conversation */
cfbdf6
+    return PAM_CONV_ERR;
cfbdf6
+}
cfbdf6
+
cfbdf6
+static pam_handle_t *
cfbdf6
+run_pam(int *pamret, const char *username, const char *display)
cfbdf6
+{
cfbdf6
+    pam_handle_t *pamh;
cfbdf6
+
cfbdf6
+    /* Say hello to PAM */
cfbdf6
+    struct pam_conv pconv;
cfbdf6
+    pconv.conv = conv;
cfbdf6
+    pconv.appdata_ptr = NULL;
cfbdf6
+    *pamret = pam_start(SERVICE_NAME, username, &pconv, &pamh);
cfbdf6
+    if (*pamret != PAM_SUCCESS) {
cfbdf6
+        /* pam_strerror requires a pamh argument, but if pam_start
cfbdf6
+           fails, pamh is invalid. In practice, at least the Linux
cfbdf6
+           implementation of pam_strerror does not use the pamh
cfbdf6
+           argument, but let's take care - avoid pam_strerror here. */
cfbdf6
+        syslog(LOG_CRIT, "pam_start failed: %d", *pamret);
cfbdf6
+        return NULL;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    /* ConsoleKit and systemd (and possibly others) uses this to
cfbdf6
+       determine if the session is local or not. It needs to be set to
cfbdf6
+       something that can't be interpreted as localhost. We don't know
cfbdf6
+       what the client's address is though, and that might change on
cfbdf6
+       reconnects. We also don't want to set it to some text string as
cfbdf6
+       that results in a DNS lookup with e.g. libaudit. Let's use a
cfbdf6
+       fake IPv4 address from the documentation range. */
cfbdf6
+    /* FIXME: This might throw an error on a IPv6-only host */
cfbdf6
+    *pamret = pam_set_item(pamh, PAM_RHOST, "203.0.113.20");
cfbdf6
+    if (*pamret != PAM_SUCCESS) {
cfbdf6
+        syslog(LOG_CRIT, "pam_set_item(PAM_RHOST) failed: %d (%s)",
cfbdf6
+               *pamret, pam_strerror(pamh, *pamret));
cfbdf6
+        return pamh;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+#ifdef PAM_XDISPLAY
cfbdf6
+    /* Let PAM modules use this to tag the session as a graphical one */
cfbdf6
+    *pamret = pam_set_item(pamh, PAM_XDISPLAY, display);
cfbdf6
+    /* Note: PAM_XDISPLAY is only supported by modern versions of PAM */
cfbdf6
+    if (*pamret != PAM_BAD_ITEM && *pamret != PAM_SUCCESS) {
cfbdf6
+        syslog(LOG_CRIT, "pam_set_item(PAM_XDISPLAY) failed: %d (%s)",
cfbdf6
+               *pamret, pam_strerror(pamh, *pamret));
cfbdf6
+        return pamh;
cfbdf6
+    }
cfbdf6
+#endif
cfbdf6
+
cfbdf6
+    /* Open session */
cfbdf6
+    *pamret = pam_open_session(pamh, PAM_SILENT);
cfbdf6
+    if (*pamret != PAM_SUCCESS) {
cfbdf6
+        syslog(LOG_CRIT, "pam_open_session failed: %d (%s)",
cfbdf6
+               *pamret, pam_strerror(pamh, *pamret));
cfbdf6
+        return pamh;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    return pamh;
cfbdf6
+}
cfbdf6
+
cfbdf6
+static int
cfbdf6
+stop_pam(pam_handle_t * pamh, int pamret)
cfbdf6
+{
cfbdf6
+    /* Close session */
cfbdf6
+    if (pamret == PAM_SUCCESS) {
cfbdf6
+        pamret = pam_close_session(pamh, PAM_SILENT);
cfbdf6
+        if (pamret != PAM_SUCCESS) {
cfbdf6
+            syslog(LOG_ERR, "pam_close_session failed: %d (%s)",
cfbdf6
+                   pamret, pam_strerror(pamh, pamret));
cfbdf6
+        }
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    /* If PAM was OK and we are running on a SELinux system, new
cfbdf6
+       processes images will be executed in the root context. */
cfbdf6
+
cfbdf6
+    /* Say goodbye */
cfbdf6
+    pamret = pam_end(pamh, pamret);
cfbdf6
+    if (pamret != PAM_SUCCESS) {
cfbdf6
+        /* avoid pam_strerror - we have no pamh. */
cfbdf6
+        syslog(LOG_ERR, "pam_end failed: %d", pamret);
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+    return pamret;
cfbdf6
+}
cfbdf6
+
cfbdf6
+static char **
cfbdf6
+prepare_environ(pam_handle_t * pamh)
cfbdf6
+{
cfbdf6
+    char **pam_env, **child_env, **entry;
cfbdf6
+    int orig_count, pam_count;
cfbdf6
+
cfbdf6
+    /* This function merges the normal environment with PAM's changes */
cfbdf6
+
cfbdf6
+    pam_env = pam_getenvlist(pamh);
cfbdf6
+    if (pam_env == NULL)
cfbdf6
+        return NULL;
cfbdf6
+
cfbdf6
+    /*
cfbdf6
+     * Worst case scenario is that PAM only adds variables, so allocate
cfbdf6
+     * based on that assumption.
cfbdf6
+     */
cfbdf6
+    orig_count = 0;
cfbdf6
+    for (entry = environ; *entry != NULL; entry++)
cfbdf6
+        orig_count++;
cfbdf6
+    pam_count = 0;
cfbdf6
+    for (entry = pam_env; *entry != NULL; entry++)
cfbdf6
+        pam_count++;
cfbdf6
+
cfbdf6
+    child_env = calloc(orig_count + pam_count + 1, sizeof(char *));
cfbdf6
+    if (child_env == NULL)
cfbdf6
+        return NULL;
cfbdf6
+
cfbdf6
+    memcpy(child_env, environ, sizeof(char *) * orig_count);
cfbdf6
+    for (entry = child_env; *entry != NULL; entry++) {
cfbdf6
+        *entry = strdup(*entry);
cfbdf6
+        if (*entry == NULL)     // FIXME: cleanup
cfbdf6
+            return NULL;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    for (entry = pam_env; *entry != NULL; entry++) {
cfbdf6
+        size_t varlen;
cfbdf6
+        char **orig_entry;
cfbdf6
+
cfbdf6
+        varlen = strcspn(*entry, "=") + 1;
cfbdf6
+
cfbdf6
+        /* Check for overwrite */
cfbdf6
+        for (orig_entry = child_env; *orig_entry != NULL; orig_entry++) {
cfbdf6
+            if (strncmp(*entry, *orig_entry, varlen) != 0)
cfbdf6
+                continue;
cfbdf6
+
cfbdf6
+            free(*orig_entry);
cfbdf6
+            *orig_entry = *entry;
cfbdf6
+            break;
cfbdf6
+        }
cfbdf6
+
cfbdf6
+        /* New variable? */
cfbdf6
+        if (*orig_entry == NULL) {
cfbdf6
+            /*
cfbdf6
+             * orig_entry will be pointing at the terminating entry,
cfbdf6
+             * so we can just tack it on here. The new NULL was already
cfbdf6
+             * prepared by calloc().
cfbdf6
+             */
cfbdf6
+            *orig_entry = *entry;
cfbdf6
+        }
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    return child_env;
cfbdf6
+}
cfbdf6
+
cfbdf6
+static void
cfbdf6
+switch_user(const char *username, uid_t uid, gid_t gid)
cfbdf6
+{
cfbdf6
+    // We must change group stuff first, because only root can do that.
cfbdf6
+    if (setgid(gid) < 0) {
cfbdf6
+        perror(": setgid");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    // Supplementary groups.
cfbdf6
+    if (initgroups(username, gid) < 0) {
cfbdf6
+        perror("initgroups");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    // Set euid, ruid and suid
cfbdf6
+    if (setuid(uid) < 0) {
cfbdf6
+        perror("setuid");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+}
cfbdf6
+
cfbdf6
+static void
cfbdf6
+redir_stdio(const char *homedir, const char *display)
cfbdf6
+{
cfbdf6
+    int fd;
cfbdf6
+    char hostname[HOST_NAME_MAX+1];
cfbdf6
+    char logfile[PATH_MAX];
cfbdf6
+
cfbdf6
+    fd = open("/dev/null", O_RDONLY);
cfbdf6
+    if (fd == -1) {
cfbdf6
+        perror("open");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+    if (dup2(fd, 0) == -1) {
cfbdf6
+        perror("dup2");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+    close(fd);
cfbdf6
+
cfbdf6
+    snprintf(logfile, sizeof(logfile), "%s/.vnc", homedir);
cfbdf6
+    if (mkdir(logfile, 0755) == -1) {
cfbdf6
+        if (errno != EEXIST) {
cfbdf6
+            perror("mkdir");
cfbdf6
+            _exit(EX_OSERR);
cfbdf6
+        }
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (gethostname(hostname, sizeof(hostname)) == -1) {
cfbdf6
+        perror("gethostname");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    snprintf(logfile, sizeof(logfile), "%s/.vnc/%s%s.log",
cfbdf6
+             homedir, hostname, display);
cfbdf6
+    fd = open(logfile, O_CREAT | O_WRONLY | O_TRUNC, 0644);
cfbdf6
+    if (fd == -1) {
cfbdf6
+        perror("open");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+    if ((dup2(fd, 1) == -1) || (dup2(fd, 2) == -1)) {
cfbdf6
+        perror("dup2");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+    close(fd);
cfbdf6
+}
cfbdf6
+
cfbdf6
+static void
cfbdf6
+close_fds(void)
cfbdf6
+{
cfbdf6
+    DIR *dir;
cfbdf6
+    struct dirent *entry;
cfbdf6
+
cfbdf6
+    dir = opendir("/proc/self/fd");
cfbdf6
+    if (dir == NULL) {
cfbdf6
+        perror("opendir");
cfbdf6
+        _exit(EX_OSERR);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    while ((entry = readdir(dir)) != NULL) {
cfbdf6
+        int fd;
cfbdf6
+        fd = atoi(entry->d_name);
cfbdf6
+        if (fd < 3)
cfbdf6
+            continue;
cfbdf6
+        close(fd);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    closedir(dir);
cfbdf6
+}
cfbdf6
+
cfbdf6
+static pid_t
cfbdf6
+run_script(const char *username, const char *display, char **envp)
cfbdf6
+{
cfbdf6
+    struct passwd *pwent;
cfbdf6
+    pid_t pid;
cfbdf6
+    const char *child_argv[3];
cfbdf6
+
cfbdf6
+    pwent = getpwnam(username);
cfbdf6
+    if (pwent == NULL) {
cfbdf6
+        syslog(LOG_CRIT, "getpwnam: %s", strerror(errno));
cfbdf6
+        return -1;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    pid = fork();
cfbdf6
+    if (pid < 0) {
cfbdf6
+        syslog(LOG_CRIT, "fork: %s", strerror(errno));
cfbdf6
+        return pid;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    /* two processes now */
cfbdf6
+
cfbdf6
+    if (pid > 0)
cfbdf6
+        return pid;
cfbdf6
+
cfbdf6
+    /* child */
cfbdf6
+
cfbdf6
+    switch_user(pwent->pw_name, pwent->pw_uid, pwent->pw_gid);
cfbdf6
+
cfbdf6
+    close_fds();
cfbdf6
+
cfbdf6
+    redir_stdio(pwent->pw_dir, display);
cfbdf6
+
cfbdf6
+    // execvpe() is not POSIX and is missing from older glibc
cfbdf6
+    // First clear out everything
cfbdf6
+    while ((environ != NULL) && (*environ != NULL)) {
cfbdf6
+        char *var, *eq;
cfbdf6
+        var = strdup(*environ);
cfbdf6
+        eq = strchr(var, '=');
cfbdf6
+        if (eq)
cfbdf6
+            *eq = '\0';
cfbdf6
+        unsetenv(var);
cfbdf6
+        free(var);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    // Then copy over the desired environment
cfbdf6
+    for (; *envp != NULL; envp++)
cfbdf6
+        putenv(*envp);
cfbdf6
+
cfbdf6
+    // Set up some basic environment for the script
cfbdf6
+    setenv("HOME", pwent->pw_dir, 1);
cfbdf6
+    setenv("SHELL", pwent->pw_shell, 1);
cfbdf6
+    setenv("LOGNAME", pwent->pw_name, 1);
cfbdf6
+    setenv("USER", pwent->pw_name, 1);
cfbdf6
+    setenv("USERNAME", pwent->pw_name, 1);
cfbdf6
+
cfbdf6
+    child_argv[0] = CMAKE_INSTALL_FULL_LIBEXECDIR "/vncserver";
cfbdf6
+    child_argv[1] = display;
cfbdf6
+    child_argv[2] = NULL;
cfbdf6
+
cfbdf6
+    execvp(child_argv[0], (char*const*)child_argv);
cfbdf6
+
cfbdf6
+    // execvp failed
cfbdf6
+    perror("execvp");
cfbdf6
+
cfbdf6
+    _exit(EX_OSERR);
cfbdf6
+}
cfbdf6
+
cfbdf6
+int
cfbdf6
+main(int argc, char **argv)
cfbdf6
+{
cfbdf6
+    char pid_file[PATH_MAX];
cfbdf6
+    FILE *f;
cfbdf6
+
cfbdf6
+    const char *username, *display;
cfbdf6
+
cfbdf6
+    if ((argc != 3) || (argv[2][0] != ':')) {
cfbdf6
+        fprintf(stderr, "Syntax:\n");
cfbdf6
+        fprintf(stderr, "    %s <username> <display>\n", argv[0]);
cfbdf6
+        return EX_USAGE;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    username = argv[1];
cfbdf6
+    display = argv[2];
cfbdf6
+
cfbdf6
+    if (geteuid() != 0) {
cfbdf6
+        fprintf(stderr, "This program needs to be run as root!\n");
cfbdf6
+        return EX_USAGE;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (getpwnam(username) == NULL) {
cfbdf6
+        if (errno == 0)
cfbdf6
+          fprintf(stderr, "User \"%s\" does not exist\n", username);
cfbdf6
+        else
cfbdf6
+          fprintf(stderr, "Cannot look up user \"%s\": %s\n",
cfbdf6
+                  username, strerror(errno));
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    if (begin_daemon() == -1)
cfbdf6
+        return EX_OSERR;
cfbdf6
+
cfbdf6
+    openlog("vncsession", LOG_PID, LOG_AUTH);
cfbdf6
+
cfbdf6
+    /* Indicate that this is a graphical user session. We need to do
cfbdf6
+       this here before PAM as pam_systemd.so looks at these. */
cfbdf6
+    if ((putenv("XDG_SESSION_CLASS=user") < 0) ||
cfbdf6
+        (putenv("XDG_SESSION_TYPE=x11") < 0)) {
cfbdf6
+        syslog(LOG_CRIT, "putenv: %s", strerror(errno));
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    /* Init PAM */
cfbdf6
+    int pamret;
cfbdf6
+    pam_handle_t *pamh = run_pam(&pamret, username, display);
cfbdf6
+    if (!pamh) {
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+    if (pamret != PAM_SUCCESS) {
cfbdf6
+        stop_pam(pamh, pamret);
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    char **child_env;
cfbdf6
+    child_env = prepare_environ(pamh);
cfbdf6
+    if (child_env == NULL) {
cfbdf6
+        syslog(LOG_CRIT, "Failure creating child process environment");
cfbdf6
+        stop_pam(pamh, pamret);
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    setup_signals();
cfbdf6
+
cfbdf6
+    script = run_script(username, display, child_env);
cfbdf6
+    if (script == -1) {
cfbdf6
+        syslog(LOG_CRIT, "Failure starting vncserver script");
cfbdf6
+        stop_pam(pamh, pamret);
cfbdf6
+        return EX_OSERR;
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    snprintf(pid_file, sizeof(pid_file),
cfbdf6
+             "/var/run/vncsession-%s.pid", display);
cfbdf6
+    f = fopen(pid_file, "w");
cfbdf6
+    if (f == NULL) {
cfbdf6
+        syslog(LOG_ERR, "Failure creating pid file \"%s\": %s",
cfbdf6
+               pid_file, strerror(errno));
cfbdf6
+    } else {
cfbdf6
+        fprintf(f, "%ld\n", (long)getpid());
cfbdf6
+        fclose(f);
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    finish_daemon();
cfbdf6
+
cfbdf6
+    while (1) {
cfbdf6
+        int status;
cfbdf6
+        pid_t gotpid = waitpid(script, &status, 0);
cfbdf6
+        if (gotpid < 0) {
cfbdf6
+            if (errno != EINTR) {
cfbdf6
+                syslog(LOG_CRIT, "waitpid: %s", strerror(errno));
cfbdf6
+                exit(EXIT_FAILURE);
cfbdf6
+            }
cfbdf6
+            continue;
cfbdf6
+        }
cfbdf6
+        if (WIFEXITED(status)) {
cfbdf6
+            if (WEXITSTATUS(status) != 0) {
cfbdf6
+                syslog(LOG_WARNING,
cfbdf6
+                        "vncsession: vncserver exited with status=%d",
cfbdf6
+                        WEXITSTATUS(status));
cfbdf6
+            }
cfbdf6
+            break;
cfbdf6
+        }
cfbdf6
+        else if (WIFSIGNALED(status)) {
cfbdf6
+            syslog(LOG_WARNING,
cfbdf6
+                    "vncsession: vncserver was terminated by signal %d",
cfbdf6
+                    WTERMSIG(status));
cfbdf6
+            break;
cfbdf6
+        }
cfbdf6
+    }
cfbdf6
+
cfbdf6
+    unlink(pid_file);
cfbdf6
+
cfbdf6
+    stop_pam(pamh, pamret);
cfbdf6
+
cfbdf6
+    return 0;
cfbdf6
+} 
cfbdf6
diff --git a/unix/vncserver/vncsession.man b/unix/vncserver/vncsession.man
cfbdf6
new file mode 100644
cfbdf6
index 0000000..2138209
cfbdf6
--- /dev/null
cfbdf6
+++ b/unix/vncserver/vncsession.man
cfbdf6
@@ -0,0 +1,75 @@
cfbdf6
+.TH vncsession 8 "" "TigerVNC" "Virtual Network Computing"
cfbdf6
+.SH NAME
cfbdf6
+vncsession \- start a VNC server
cfbdf6
+.SH SYNOPSIS
cfbdf6
+.B vncsession
cfbdf6
+.RI < username >
cfbdf6
+.RI <: display# >
cfbdf6
+.SH DESCRIPTION
cfbdf6
+.B vncsession
cfbdf6
+is used to start a VNC (Virtual Network Computing) desktop.
cfbdf6
+.B vncsession
cfbdf6
+performs all the necessary steps to create a new user session, run Xvnc with
cfbdf6
+appropriate options and starts a window manager on the VNC desktop.
cfbdf6
+
cfbdf6
+.B vncsession
cfbdf6
+is rarely called directly and is normally started by the system service
cfbdf6
+manager.
cfbdf6
+
cfbdf6
+.SH FILES
cfbdf6
+Several VNC-related files are found in the directory $HOME/.vnc:
cfbdf6
+.TP
cfbdf6
+/etc/tigervnc/vncserver-config-defaults
cfbdf6
+The optional system-wide equivalent of $HOME/.vnc/config. If this file exists
cfbdf6
+and defines options to be passed to Xvnc, they will be used as defaults for
cfbdf6
+users. The user's $HOME/.vnc/config overrides settings configured in this file.
cfbdf6
+The overall configuration file load order is: this file, $HOME/.vnc/config,
cfbdf6
+and then /etc/tigervnc/vncserver-config-mandatory. None are required to exist.
cfbdf6
+.TP
cfbdf6
+/etc/tigervnc/vncserver-config-mandatory
cfbdf6
+The optional system-wide equivalent of $HOME/.vnc/config. If this file exists
cfbdf6
+and defines options to be passed to Xvnc, they will override any of the same
cfbdf6
+options defined in a user's $HOME/.vnc/config. This file offers a mechanism
cfbdf6
+to establish some basic form of system-wide policy. WARNING! There is
cfbdf6
+nothing stopping users from constructing their own vncsession-like script
cfbdf6
+that calls Xvnc directly to bypass any options defined in
cfbdf6
+/etc/tigervnc/vncserver-config-mandatory. The overall configuration file load
cfbdf6
+order is: /etc/tigervnc/vncserver-config-defaults, $HOME/.vnc/config, and then
cfbdf6
+this file. None are required to exist.
cfbdf6
+.TP
cfbdf6
+$HOME/.vnc/config
cfbdf6
+An optional server config file wherein options to be passed to Xvnc are listed
cfbdf6
+to avoid hard-coding them to the physical invocation. List options in this file
cfbdf6
+one per line. For those requiring an argument, simply separate the option from
cfbdf6
+the argument with an equal sign, for example: "geometry=2000x1200" or
cfbdf6
+"securitytypes=vncauth,tlsvnc". Options without an argument are simply listed
cfbdf6
+as a single word, for example: "localhost" or "alwaysshared".
cfbdf6
+
cfbdf6
+The special option
cfbdf6
+.B session
cfbdf6
+can be used to control which session type will be started. This should match
cfbdf6
+one of the files in \fI/usr/share/xsessions\fP. E.g. if there is a file called
cfbdf6
+"gnome.desktop", then "session=gnome" would be set to use that session type.
cfbdf6
+.TP
cfbdf6
+$HOME/.vnc/passwd
cfbdf6
+The VNC password file.
cfbdf6
+.TP
cfbdf6
+$HOME/.vnc/\fIhost\fP:\fIdisplay#\fP.log
cfbdf6
+The log file for Xvnc and the session.
cfbdf6
+
cfbdf6
+.SH SEE ALSO
cfbdf6
+.BR vncviewer (1),
cfbdf6
+.BR vncpasswd (1),
cfbdf6
+.BR vncconfig (1),
cfbdf6
+.BR Xvnc (1)
cfbdf6
+.br
cfbdf6
+https://www.tigervnc.org
cfbdf6
+
cfbdf6
+.SH AUTHOR
cfbdf6
+Tristan Richardson, RealVNC Ltd., D. R. Commander and others.
cfbdf6
+
cfbdf6
+VNC was originally developed by the RealVNC team while at Olivetti
cfbdf6
+Research Ltd / AT&T Laboratories Cambridge.  TightVNC additions were
cfbdf6
+implemented by Constantin Kaplinsky. Many other people have since
cfbdf6
+participated in development, testing and support. This manual is part
cfbdf6
+of the TigerVNC software suite.
cfbdf6
diff --git a/unix/x0vncserver/CMakeLists.txt b/unix/x0vncserver/CMakeLists.txt
cfbdf6
index 8beade7..af82415 100644
cfbdf6
--- a/unix/x0vncserver/CMakeLists.txt
cfbdf6
+++ b/unix/x0vncserver/CMakeLists.txt
cfbdf6
@@ -52,5 +52,5 @@ endif()
cfbdf6
 
cfbdf6
 target_link_libraries(x0vncserver ${X11_LIBRARIES})
cfbdf6
 
cfbdf6
-install(TARGETS x0vncserver DESTINATION ${BIN_DIR})
cfbdf6
-install(FILES x0vncserver.man DESTINATION ${MAN_DIR}/man1 RENAME x0vncserver.1)
cfbdf6
+install(TARGETS x0vncserver DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
cfbdf6
+install(FILES x0vncserver.man DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1 RENAME x0vncserver.1)
cfbdf6
diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man
cfbdf6
index 9c8a889..9559179 100644
cfbdf6
--- a/unix/xserver/hw/vnc/Xvnc.man
cfbdf6
+++ b/unix/xserver/hw/vnc/Xvnc.man
cfbdf6
@@ -18,9 +18,9 @@ that the VNC server display number will be the same as the X server display
cfbdf6
 number, which means you can use eg. snoopy:2 to refer to display 2 on machine
cfbdf6
 "snoopy" in both the X world and the VNC world.
cfbdf6
 
cfbdf6
-The best way of starting \fBXvnc\fP is via the \fBvncserver\fP script.  This
cfbdf6
-sets up the environment appropriately and runs some X applications to get you
cfbdf6
-going.  See the manual page for \fBvncserver\fP(1) for more information.
cfbdf6
+The best way of starting \fBXvnc\fP is via \fBvncsession\fP.  This sets up the
cfbdf6
+environment appropriately and starts a desktop environment. See the manual
cfbdf6
+page for \fBvncsession\fP(8) for more information.
cfbdf6
 
cfbdf6
 .SH OPTIONS
cfbdf6
 .B Xvnc
cfbdf6
@@ -383,8 +383,8 @@ created automatically the next time he connects.
cfbdf6
 .SH SEE ALSO
cfbdf6
 .BR vncconfig (1),
cfbdf6
 .BR vncpasswd (1),
cfbdf6
-.BR vncserver (1),
cfbdf6
 .BR vncviewer (1),
cfbdf6
+.BR vncsession (8),
cfbdf6
 .BR Xserver (1),
cfbdf6
 .BR inetd (1)
cfbdf6
 .br
cfbdf6
diff --git a/vncviewer/CMakeLists.txt b/vncviewer/CMakeLists.txt
cfbdf6
index 3c18646..7e25d45 100644
cfbdf6
--- a/vncviewer/CMakeLists.txt
cfbdf6
+++ b/vncviewer/CMakeLists.txt
cfbdf6
@@ -60,9 +60,9 @@ if(APPLE)
cfbdf6
   target_link_libraries(vncviewer "-framework IOKit")
cfbdf6
 endif()
cfbdf6
 
cfbdf6
-install(TARGETS vncviewer DESTINATION ${BIN_DIR})
cfbdf6
+install(TARGETS vncviewer DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
cfbdf6
 if(UNIX)
cfbdf6
-  install(FILES vncviewer.man DESTINATION ${MAN_DIR}/man1 RENAME vncviewer.1)
cfbdf6
+  install(FILES vncviewer.man DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1 RENAME vncviewer.1)
cfbdf6
 
cfbdf6
   configure_file(vncviewer.desktop.in.in vncviewer.desktop.in)
cfbdf6
   find_program(INTLTOOL_MERGE_EXECUTABLE intltool-merge)
cfbdf6
@@ -91,10 +91,10 @@ if(UNIX)
cfbdf6
     )
cfbdf6
   endif()
cfbdf6
   add_custom_target(desktop ALL DEPENDS vncviewer.desktop)
cfbdf6
-  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/vncviewer.desktop DESTINATION ${DATA_DIR}/applications)
cfbdf6
+  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/vncviewer.desktop DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/applications)
cfbdf6
 
cfbdf6
   foreach(res 16 22 24 32 48)
cfbdf6
-    install(FILES ../media/icons/tigervnc_${res}.png DESTINATION ${DATA_DIR}/icons/hicolor/${res}x${res}/apps RENAME tigervnc.png)
cfbdf6
+    install(FILES ../media/icons/tigervnc_${res}.png DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/${res}x${res}/apps RENAME tigervnc.png)
cfbdf6
   endforeach()
cfbdf6
-  install(FILES ../media/icons/tigervnc.svg DESTINATION ${DATA_DIR}/icons/hicolor/scalable/apps)
cfbdf6
+  install(FILES ../media/icons/tigervnc.svg DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/scalable/apps)
cfbdf6
 endif()
cfbdf6
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
cfbdf6
index 4a8370b..ae4cb6f 100644
cfbdf6
--- a/vncviewer/vncviewer.cxx
cfbdf6
+++ b/vncviewer/vncviewer.cxx
cfbdf6
@@ -227,7 +227,7 @@ static void init_fltk()
cfbdf6
       bool exists;
cfbdf6
 
cfbdf6
       sprintf(icon_path, "%s/icons/hicolor/%dx%d/apps/tigervnc.png",
cfbdf6
-              DATA_DIR, icon_sizes[i], icon_sizes[i]);
cfbdf6
+              CMAKE_INSTALL_FULL_DATADIR, icon_sizes[i], icon_sizes[i]);
cfbdf6
 
cfbdf6
 #ifndef WIN32
cfbdf6
       struct stat st;
cfbdf6
@@ -505,7 +505,7 @@ int main(int argc, char** argv)
cfbdf6
   argv0 = argv[0];
cfbdf6
 
cfbdf6
   setlocale(LC_ALL, "");
cfbdf6
-  bindtextdomain(PACKAGE_NAME, LOCALE_DIR);
cfbdf6
+  bindtextdomain(PACKAGE_NAME, CMAKE_INSTALL_FULL_LOCALEDIR);
cfbdf6
   textdomain(PACKAGE_NAME);
cfbdf6
 
cfbdf6
   rfb::SecurityClient::setDefaults();
cfbdf6
diff --git a/vncviewer/vncviewer.desktop.in.in b/vncviewer/vncviewer.desktop.in.in
cfbdf6
index d775dde..9d658e4 100644
cfbdf6
--- a/vncviewer/vncviewer.desktop.in.in
cfbdf6
+++ b/vncviewer/vncviewer.desktop.in.in
cfbdf6
@@ -2,7 +2,7 @@
cfbdf6
 Name=TigerVNC Viewer
cfbdf6
 GenericName=Remote Desktop Viewer
cfbdf6
 Comment=Connect to VNC server and display remote desktop
cfbdf6
-Exec=@BIN_DIR@/vncviewer
cfbdf6
+Exec=@CMAKE_INSTALL_FULL_BINDIR@/vncviewer
cfbdf6
 Icon=tigervnc
cfbdf6
 Terminal=false
cfbdf6
 Type=Application
cfbdf6
diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man
cfbdf6
index f93e096..a8d8c77 100644
cfbdf6
--- a/vncviewer/vncviewer.man
cfbdf6
+++ b/vncviewer/vncviewer.man
cfbdf6
@@ -327,7 +327,7 @@ Default certificate revocation list.
cfbdf6
 .BR Xvnc (1),
cfbdf6
 .BR vncpasswd (1),
cfbdf6
 .BR vncconfig (1),
cfbdf6
-.BR vncserver (1)
cfbdf6
+.BR vncsession (8)
cfbdf6
 .br
cfbdf6
 https://www.tigervnc.org
cfbdf6