Blame SOURCES/tigervnc-systemd-support.patch

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