Blame SOURCES/tigervnc-systemd-support.patch

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