jupyterhub / jupyter-remote-desktop-proxy

Run a Linux Desktop on a JupyterHub
BSD 3-Clause "New" or "Revised" License
116 stars 106 forks source link

TigerVNC check does not work for new versions of TigerVNC #69

Closed goekce closed 9 months ago

goekce commented 1 year ago

Bug description

I am on Arch Linux. I installed my own TigerVNC, because the bundled TigerVNC segfaults when I start Firefox.

jupyter-remote-desktop-proxy picks the configuration based on whether the string TigerVNC can be found in vncserver:

https://github.com/jupyterhub/jupyter-remote-desktop-proxy/blob/024ab7d61dbe403dc10b090bde4eb2239b030d8e/jupyter_remote_desktop_proxy/__init__.py#L25

The newest version of vncserver does not include TigerVNC as a string, which can lead to wrong configuration of the installed VNC server.

The workaround is to use tigervnc as a string instead of TigerVNC. I can happily prepare a PR, however I also saw that there is a discussion about which vncservers to support, so I wanted to confirm if my workaround makes sense.

I can include more details, if the error description is not clear.


A general remark about configurability:

The configuration can change between the software versions, so a more sustainable solution would be to make the command for starting the VNC session configurable. Currently the config is mostly hardcoded. Another argument that calls for a configuration capability: the newest vncserver does not support -xstartup argument used here:

https://github.com/jupyterhub/jupyter-remote-desktop-proxy/blob/024ab7d61dbe403dc10b090bde4eb2239b030d8e/jupyter_remote_desktop_proxy/__init__.py#L34-L35

I can open a new issue for this if needed.

welcome[bot] commented 1 year ago

Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! :hugs:
If you haven't done so already, check out Jupyter's Code of Conduct. Also, please try to follow the issue template as it helps other other community members to contribute more effectively. welcome You can meet the other Jovyans by joining our Discourse forum. There is also an intro thread there where you can stop by and say Hi! :wave:
Welcome to the Jupyter community! :tada:

consideRatio commented 9 months ago

@goekce this is an amazing issue writeup! Thank you!!!

I think the short term fix at least should be to look if tiger is in the lower case version of the string, the check works for old/new versions.

goekce commented 9 months ago

Currently I don't have time for a PR. Someone else is welcome to take over the pull request.

consideRatio commented 9 months ago

Both TurboVNC (vncserver.in) and TigerVNC (vncserver.in) has vncserver.in, but Tiger as installed via apt doesn't show that directly while Turbo does. I've not yet regonized the vncserver file I've seen for Tiger when installing it via apt, but I assume its specific to how it was distributed in apt.

I think we should still check for tigervnc in a case insensitive way, but I'm hoping to understand this well enough to fix it robustly.

@goekce Is the file you see when doing cat $(which vncserver) vncserver.in?

Content from two images installing tiger/turbo vnc

docker run -it --entrypoint bash quay.io/jupyterhub/jupyter-remote-desktop-proxy:main-tigervnc -c 'cat $(which vncserver)' > vncserver-tiger.txt
docker run -it --entrypoint bash quay.io/jupyterhub/jupyter-remote-desktop-proxy:main-turbovnc -c 'cat $(which vncserver)' > vncserver-turbo.txt
cat $(which vncserver) inside tigervnc image ```perl #! /usr/bin/perl # vim: set sw=2 sts=2 ts=8 syn=perl expandtab: # # tigervncserver - wrapper script to start a standalone X VNC server. # # Copyright (C) 2021 Joachim Falk # # This is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this software; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. use warnings; use strict; use TigerVNC::Common; use TigerVNC::Config; use TigerVNC::Wrapper; sub main { my $options = { wrapperMode => 'tigervncserver' }; # # First, we ensure that we're operating in a sane environment. # exit 1 unless &sanityCheck($options); # # Next, parses the system /etc/tigervnc/vncserver-config-defaults and the user # ~/.vnc/tigervnc.conf configuration file as well as processes the command line. # &getConfig($options); if ($options->{'usageError'} || $options->{'help'}) { &usage($options); exit($options->{'usageError'} ? 1 : 0); } unless (defined $options->{'displayHost'}) { $options->{'displayHost'} = $HOSTFQDN; } if ($options->{'remote'}) { my @cmdPrefix = ("ssh", "$options->{'displayHost'}", "tigervncserver"); # Get rid of possible user@ in front of displayHost. $options->{'displayHost'} =~ s/^[^@]*@//; my @cmd; if ( $options->{'kill'} ) { push @cmd, "-kill"; push @cmd, ":$options->{'displayNumber'}" if defined $options->{'displayNumber'}; push @cmd, "-dry-run" if $options->{'dry-run'}; push @cmd, "-clean" if ($options->{'clean'}); } elsif ( $options->{'list'} ) { push @cmd, "-list"; push @cmd, ":$options->{'displayNumber'}" if defined $options->{'displayNumber'}; push @cmd, "-cleanstale" if ($options->{'cleanstale'}); } elsif ( $options->{'version'} ) { push @cmd, "-version"; } else { push @cmd, ":$options->{'displayNumber'}" if defined $options->{'displayNumber'}; my $userOptions = { wrapperMode => 'tigervncserver' }; while (my ($key, $value) = each %{$options}) { my $src = $options->{'src'}->{$key} // "undef"; if ($src eq 'user' || $src eq 'cmdline') { $userOptions->{'src'}->{$key} = $src; $userOptions->{$key} = $value; } } $userOptions->{'vncServerExtraArgs'} = [grep { $_->{'src'} eq 'user' || $_->{'src'} eq 'cmdline'; } @{$options->{'vncServerExtraArgs'}}]; my @session; foreach my $optionParseEntry (@{&getOptionParseTable($userOptions)}) { my ($flags, $optname, $store) = @{$optionParseEntry}; next unless $flags & &OPT_TIGERVNCSERVER; $optname =~ m/^([^:=|]*)/; my $name = $1; my $value = &{$store}($name); if ($name eq 'session') { @session = @{$value} if defined $value; next; # Session is a pseudo option, it's given via -- . } # Display is already handled next if $name eq 'display'; if ($optname =~ m/:/) { push @cmd, "-$name=$value" if defined $value; } elsif ($optname =~ m/=/) { push @cmd, "-$name", $value if defined $value; } elsif (!($optname =~ m/[:=]/)) { push @cmd, "-$name" if $value; } else { die "Oops, can't parse $optname format!"; } } push @cmd, map { @{$_->{'args'}} } @{$userOptions->{'vncServerExtraArgs'}}; push @cmd, '--', @session if @session > 0; } @cmd = (@cmdPrefix, map { "edString($_); } @cmd); print join(" ",@cmd), "\n" if $options->{'verbose'}; if (system (@cmd)) { print STDERR "\n$PROG: Command '", join(" ", @cmd), "' failed: $?\n"; exit -1; } if (!$options->{'kill'} && !$options->{'list'} && !$options->{'version'}) { # Feedback on how to connect to the remote tigervnc server. print "Use xtigervncviewer -via $options->{'displayHost'} :n to connect!\n"; print "The display number :n is given in the above startup message from tigervncserver.\n"; } exit 0; } if ($options->{'kill'}) { my $err = &killVncServers($options); exit($err ? 1 : 0); } elsif ($options->{'list'}) { &listVncServers(\*STDOUT, $options); exit 0; } elsif ($options->{'version'}) { my @cmd = (&getCommand("Xtigervnc"), "-version"); if (system {$cmd[0]} (@cmd)) { exit 1; } else { exit 0; } } else { exit &startVncServer($options); } } &main; ```
cat $(which vncserver) inside turbovnc image ```perl #!/usr/bin/env perl # # Copyright (C) 2009-2018, 2020-2022, 2024 D. R. Commander. # All Rights Reserved. # Copyright (C) 2021 Steffen Kieß # Copyright (C) 2010 University Corporation for Atmospheric Research. # All Rights Reserved. # Copyright (C) 2002-2009 Constantin Kaplinsky. All Rights Reserved. # Copyright (C) 2005-2006 Sun Microsystems, Inc. All Rights Reserved. # Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. # Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. # # This is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this software; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. # # vncserver - wrapper script to start the TurboVNC X server. # # First make sure we're operating in a sane environment. $exedir = ""; $slashndx = rindex($0, "/"); if ($slashndx >= 0) { $exedir = substr($0, 0, $slashndx + 1); } $xauth = "xauth"; $buildWebServer = 1; $staticXorgPaths = 0; &SanityCheck(); # Default configuration of the TurboVNC Server: $geometry = "1240x900"; $depth = 24; $vncUserDir = "$ENV{HOME}/.vnc"; $authTypeVNC = 1; $authTypeOTP = 1; $generateOTP = 0; $securityTypes = ""; $encTypeX509 = 1; $noxstartup = 0; $autoLosslessRefresh = 0.0; $deferUpdate = 1; $wm = ""; $useVGL = 0; $autokill = 1; $pamSession = 0; $bits = 64; $multiThread = 1; $numThreads = 0; $noVNC = ""; $serverArgs = ""; $useUDS = 0; $udsPath = ""; # Read configuration from the system-wide and user files if present. $configFile = "/etc/turbovncserver.conf"; ReadConfiguration(); $configFile = "$ENV{HOME}/.vnc/turbovncserver.conf"; ReadConfiguration(); ReadAuthConfiguration("/etc/turbovncserver-security.conf"); # We set these defaults after reading the configuration file, in case # $vncUserDir was modified. $passwdFile = "$vncUserDir/passwd" if (!$passwdFile); $x509CertFile = "$vncUserDir/x509_cert.pem" if (!$x509CertFile); $x509KeyFile = "$vncUserDir/x509_private.pem" if (!$x509KeyFile); # Done reading configuration. $xauthorityFile = "$ENV{XAUTHORITY}"; if (!$xstartup) { $xstartup = $exedir."xstartup.turbovnc"; } $vncUserDirUnderTmp = ($vncUserDir =~ m|^/tmp/.+|) ? 1 : 0; unless ($xauthorityFile) { if ($vncUserDirUnderTmp) { $xauthorityFile = "$vncUserDir/.Xauthority"; } else { $xauthorityFile = "$ENV{HOME}/.Xauthority"; } } chop($host = `uname -n`); chop($os = `uname`); if (!$staticXorgPaths) { if (-d "/etc/X11/fontpath.d") { $fontPath = "catalogue:/etc/X11/fontpath.d"; } @fontpaths = ('/usr/share/X11/fonts', '/usr/share/fonts', '/usr/share/fonts/X11', '/usr/local/lib/X11/fonts', '/usr/local/share/fonts'); if (! -l "/usr/lib/X11") { push(@fontpaths, '/usr/lib/X11/fonts'); } if (! -l "/usr/X11") { push(@fontpaths, '/usr/X11/lib/X11/fonts'); } if (! -l "/usr/openwin") { push(@fontpaths, '/usr/openwin/lib/X11/fonts'); } if (! -l "/usr/X11R6") { push(@fontpaths, '/usr/X11R6/lib/X11/fonts'); } if (! -l "/opt/X11/share/fonts") { push(@fontpaths, '/opt/X11/share/fonts'); } push(@fontpaths, '/usr/share/fonts/default'); @fonttypes = ('F3bitmaps', 'misc', '75dpi', '100dpi', 'Speedo', 'Type1', 'ghostscript', 'liberation', 'TTF', 'OTF'); if (($fontPath eq "")) { foreach $_fpath (@fontpaths) { if (-f "$_fpath/encodings/encodings.dir") { $ENV{FONT_ENCODINGS_DIRECTORY} = "$_fpath/encodings"; } foreach $_ftype (@fonttypes) { if (-f "$_fpath/$_ftype/fonts.dir") { if (! -l "$_fpath/$_ftype") { $fontPath .= "$_fpath/$_ftype,"; } } } } } if ($fontPath) { if (substr($fontPath, -1, 1) eq ',') { chop $fontPath; } } if ($os eq "Darwin") { $fontPath = "$fontPath,/Library/Fonts,/System/Library/Fonts"; } @xkbdirs = ('/usr/X11R6/lib/X11/xkb', '/usr/local/share/X11/xkb', '/opt/X11/share/X11/xkb'); foreach $_xkbdir (@xkbdirs) { if (-d "$_xkbdir") { $xkbdir = "$_xkbdir"; } } @xkbcompdirs = ('/usr/X11R6/bin', '/usr/X11/lib/X11/xkb', '/usr/local/bin', '/opt/X11/bin'); foreach $_xkbcompdir (@xkbcompdirs) { if (-x "$_xkbcompdir/xkbcomp") { $xkbcompdir = "$_xkbcompdir"; } } if ($bits eq "64") { @dridirs = ('/usr/lib64/dri', '/usr/lib/dri', '/usr/lib/x86_64-linux-gnu/dri', '/usr/lib/xorg/modules/dri/amd64', '/usr/lib/powerpc64-linux-gnu/dri', '/usr/lib/powerpc64le-linux-gnu/dri', '/usr/lib/aarch64-linux-gnu/dri'); } else { @dridirs = ('/usr/lib/dri', '/usr/lib32/dri', '/usr/lib/i386-linux-gnu/dri', '/usr/lib/xorg/modules/dri', '/usr/lib/arm-linux-gnueabihf/dri', '/usr/lib/arm-linux-gnueabi/dri'); } push(@dridirs, '/usr/local/lib/dri'); foreach $_dridir (@dridirs) { if (-f "$_dridir/swrast_dri.so") { $dridir = "$_dridir"; last; } } @registrydirs = ('/usr/lib/xorg'); if ($bits eq "64") { push(@registrydirs, '/usr/lib64/xorg'); } push(@registrydirs, '/usr/local/lib/xorg'); push(@registrydirs, '/opt/X11/lib/xorg'); foreach $_registrydir (@registrydirs) { if (-f "$_registrydir/protocol.txt") { $registrydir = "$_registrydir"; last; } } } # !$staticXorgPaths # Check command line options &ParseOptions("-geometry", 1, "-depth", 1, "-pixelformat", 1, "-name", 1, "-kill", 1, "-help", 0, "-h", 0, "--help", 0, "-fg", 0, "-list", 0, "-fp", 1, "-otp", 0, "-securitytypes", 1, "-rfbauth", 1, "-noxstartup", 0, "-xstartup", 1, "-log", 1, "-3dwm", 0, "-vgl", 0, "-debug", 0, "-x509cert", 1, "-x509key", 1, "-autokill", 0, "-quiet", 0, "-wm", 1, "-sessionlist", 0, "-sessionstart", 0, "-novnc", 1, "-noautokill", 0, "-rfbport", 1, "-rfbunixpath", 1, "-uds", 0); &Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'}); &Kill() if ($opt{'-kill'}); if ($opt{'-list'}) { &List(1); exit; } if ($opt{'-sessionlist'}) { &List(0); exit; } if (defined($ENV{WAYLAND_DISPLAY})) { warn "\nWARNING: Some window managers will not work in a TurboVNC session if the\n"; warn "TurboVNC session is started from a Wayland session.\n"; } if ($opt{'-sessionstart'}) { $opt{'-quiet'} = 1; } # Uncomment this line if you want default geometry, depth and pixelformat # to match the current X display: # &GetXDisplayDefaults(); if ($opt{'-geometry'}) { $geometry = $opt{'-geometry'}; } if ($opt{'-depth'}) { $depth = $opt{'-depth'}; $pixelformat = ""; } if ($opt{'-pixelformat'}) { $pixelformat = $opt{'-pixelformat'}; } if ($opt{'-novnc'}) { if (!$buildWebServer) { warn "TurboVNC was not built with the noVNC web server. Ignoring -novnc.\n"; } else { $noVNC = $opt{'-novnc'}; if (! -e "$noVNC/vnc.html") { die $noVNC . " does not appear to contain an installation of noVNC.\n" } } } if ($opt{'-noxstartup'}) { $noxstartup = 1; } if ($opt{'-xstartup'}) { $xstartup = $opt{'-xstartup'}; } if ($opt{'-wm'}) { $wm = $opt{'-wm'}; } if ($opt{'-3dwm'} || $opt{'-vgl'}) { $useVGL = 1; } if ($opt{'-fp'}) { $fontPath = $opt{'-fp'}; } if ($opt{'-deferupdate'}) { $deferUpdate = $opt{'-deferupdate'}; } if ($opt{'-debug'}) { $opt{'-fg'} = 1; } if ($opt{'-noautokill'}) { $autokill = 0; } $authTypeVNCPermitted = $authTypeVNC; if ($opt{'-securitytypes'}) { $securityTypes = $opt{'-securitytypes'}; } if ($securityTypes) { my $enableVNC = 0; my $enableOTP = 0; my $secTypes = $securityTypes; $secTypes =~ s/[\t\ \r\n]//g; foreach $tok (split/[,=]/, $secTypes) { if (substr(lc $tok, -3) eq 'otp') { $enableOTP = 1; } if (substr(lc $tok, -3) eq 'vnc') { $enableVNC = 1; } } if ($enableVNC == 0) { $authTypeVNC = 0; } if ($enableOTP == 0) { $authTypeOTP = 0; } } if ($opt{'-rfbauth'}) { if ($authTypeVNC) { $passwdFile = $opt{'-rfbauth'}; } else { if ($authTypeVNCPermitted) { warn "VNC Password auth is not enabled. Ignoring -rfbauth.\n"; } else { warn "VNC Password auth is not permitted on this system. Ignoring -rfbauth.\n"; } } } if ($opt{'-x509cert'}) { if ($encTypeX509) { $x509CertFile = $opt{'-x509cert'}; } else { warn "Server was not built with TLS encryption. Ignoring -x509cert.\n"; } } if ($opt{'-x509key'}) { if ($encTypeX509) { $x509KeyFile = $opt{'-x509key'}; } else { warn "Server was not built with TLS encryption. Ignoring -x509key.\n"; } } if ($opt{'-otp'}) { $generateOTP = 1; } if ($generateOTP && $authTypeOTP == 0) { warn "One-Time Password authentication is not enabled. Ignoring request to generate\n"; warn " initial OTP.\n"; $generateOTP = 0; } &CheckGeometryAndDepth(); # Create the user's vnc directory if necessary. unless (-e $vncUserDir) { unless (mkdir($vncUserDir, 0700)) { die "$prog: Could not create $vncUserDir.\n"; } } ($z, $z, $mode) = lstat("$vncUserDir"); if (! -d _ || ! -o _ || ($vncUserDirUnderTmp && ($mode & 0777) != 0700)) { die "$prog: Wrong type or access mode of $vncUserDir.\n"; } # Make sure the user has a password. if ($authTypeVNC && "$passwdFile" eq "$vncUserDir/passwd") { ($z, $z, $mode) = lstat("$vncUserDir/passwd"); if (-e _ && (! -f _ || ! -o _ || ($mode & 077) != 0)) { die "$prog: Wrong type, ownership, or permissions on\n $vncUserDir/passwd.\n"; } if (! -e _) { if ($opt{'-quiet'}) { warn "$vncUserDir/passwd does not exist.\nRun ".$exedir."vncpasswd to create passwd file.\n"; exit 1; } else { warn "\nYou will require a password to access your desktops.\n\n"; system($exedir."vncpasswd $vncUserDir/passwd"); if (($? & 0xFF00) != 0) { exit 1; } } } } # Find display number. if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) { $displayNumber = $1; shift(@ARGV); unless (&CheckDisplayNumber($displayNumber)) { die "A VNC server is already running as :$displayNumber\n"; } } elsif ((@ARGV > 0) && ($ARGV[0] !~ /^-/) && ($ARGV[0] !~ /^\+/)) { &Usage(); } else { $displayNumber = &GetDisplayNumber(); } $vncPort = 5900 + $displayNumber; if ($opt{'-rfbport'}) { $vncPort = $opt{'-rfbport'}; } if ($opt{'-uds'}) { $useUDS = 1; } if ($opt{'-rfbunixpath'}) { $udsPath = $opt{'-rfbunixpath'}; } elsif ($useUDS) { $udsPath = "$vncUserDir/$host\_$displayNumber.uds"; } if ($opt{'-log'}) { $desktopLog = $opt{'-log'}; } else { $desktopLog = "$vncUserDir/$host:$displayNumber.log"; } unlink($desktopLog); if ($opt{'-name'}) { $desktopName = $opt{'-name'}; } else { $desktopName = "TurboVNC: $host:$displayNumber ($ENV{USER})" unless($desktopName); } # Make an X server cookie - use /dev/urandom on systems that have it, # otherwise use perl's random number generator, seeded with the sum # of the current time, our PID and part of the encrypted form of the password. my $cookie = ""; if (open(URANDOM, '<', '/dev/urandom')) { my $randata; if (sysread(URANDOM, $randata, 16) == 16) { $cookie = unpack 'h*', $randata; } close(URANDOM); } if ($cookie eq "") { if (-e "$vncUserDir/passwd") { srand(time + $$ + unpack("L", `cat $vncUserDir/passwd`)); } else { srand(time + $$); } for (1..16) { $cookie .= sprintf("%02x", int(rand(256)) % 256); } } system("$xauth -f $xauthorityFile add $host:$displayNumber . $cookie"); system("$xauth -f $xauthorityFile add $host/unix:$displayNumber . $cookie"); if ($vncUserDirUnderTmp) { system("$xauth merge $xauthorityFile"); } # Now start the TurboVNC X server $cmd = $exedir."Xvnc :$displayNumber"; $cmd .= " -dpi $dpi" if ($dpi); $cmd .= " -desktop " . "edString($desktopName); $cmd .= " -auth $xauthorityFile"; $cmd .= " -geometry $geometry" if ($geometry); $cmd .= " -depth $depth" if ($depth); $cmd .= " -pixelformat $pixelformat" if ($pixelformat); $cmd .= " -rfbauth $passwdFile" if ($authTypeVNC); $cmd .= " -x509cert $x509CertFile" if ($encTypeX509); $cmd .= " -x509key $x509KeyFile" if ($encTypeX509); $cmd .= " -securitytypes \"$securityTypes\"" if ($securityTypes); $cmd .= " -rfbport $vncPort"; $cmd .= " -rfbunixpath " . "edString($udsPath) if ($udsPath); $cmd .= " -fp $fontPath" if ($fontPath); $cmd .= " -alr ".$autoLosslessRefresh if ($autoLosslessRefresh > 0.0); $cmd .= " -deferupdate $deferUpdate"; $cmd .= " -xkbdir $xkbdir" if ($xkbdir && !$staticXorgPaths); $cmd .= " -xkbcompdir $xkbcompdir" if ($xkbcompdir && !$staticXorgPaths); $cmd .= " -pamsession" if ($pamSession); $cmd .= " -dridir $dridir" if ($dridir && !$staticXorgPaths); $cmd .= " -registrydir $registrydir" if ($registrydir && !$staticXorgPaths); $cmd .= " -nomt" if (!$multiThread); $cmd .= " -nthreads $numThreads" if ($numThreads); $cmd .= " $serverArgs" if ($serverArgs); foreach $arg (@ARGV) { $cmd .= " " . "edString($arg); } if (!$opt{'-debug'}) { $cmd .= " >> " . "edString($desktopLog) . " 2>&1"; } # Run $cmd and record the process ID. $pidFile = "$vncUserDir/$host:$displayNumber.pid"; system("$cmd & echo \$! >$pidFile"); # Record the RFB Unix domain socket path $udsPathFile = "$vncUserDir/$host:$displayNumber.udspath"; if ($udsPath) { open(UDSPATHFILE, '>', $udsPathFile); print UDSPATHFILE "$udsPath\n" } else { unlink $udsPathFile; } # Give Xvnc a chance to start up sleep(1); unless (kill 0, `cat $pidFile`) { # If Xvnc exits on startup, it might be because the RFB Unix domain socket # belongs to another process. Remove the .udspath file to prevent -kill from # attempting to remove the Unix domain socket later. if ($udsPath) { unlink $udsPathFile; } warn "Could not start Xvnc.\n\n"; open(LOG, "<$desktopLog"); while () { print; } close(LOG); die "\n"; } if (!$opt{'-sessionstart'}) { warn "\nDesktop '$desktopName' started on display $host:$displayNumber\n"; if ($udsPath) { warn "Listening on Unix domain socket $udsPath\n"; } warn "\n"; } if ($generateOTP == 1) { warn "One-Time Password authentication enabled. Generating initial OTP ...\n"; system($exedir."vncpasswd -o -display :$displayNumber"); if (($? & 0xFF00) != 0) { warn "Could not generate initial OTP.\n"; exit 1; } warn "Run '".$exedir."vncpasswd -o' from within the TurboVNC session or\n '".$exedir."vncpasswd -o -display :$displayNumber' from within this shell\n to generate additional OTPs\n"; } # Start noVNC web server and record the process ID. if ($noVNC) { $noVNCEncrypt = $encTypeX509; $cmd = $exedir."webserver"; $cmd .= " -dir " . $noVNC; $cmd .= " -httpport " . (5800 + $displayNumber); if ($noVNCEncrypt) { if (! -e $x509CertFile) { warn $x509CertFile . " does not exist. Disabling noVNC encryption.\n"; $noVNCEncrypt = 0; } else { $cmd .= " -x509cert " . $x509CertFile; } if (! -e $x509KeyFile) { warn $x509KeyFile . " does not exist. Disabling noVNC encryption.\n"; $noVNCEncrypt = 0; } else { $cmd .= " -x509key " . $x509KeyFile; } } if (!$opt{'-debug'}) { $cmd .= " >> " . "edString($desktopLog) . " 2>&1"; } $noVNCPIDFile = "$vncUserDir/$host:$displayNumber-noVNC.pid"; system("$cmd & echo \$! >$noVNCPIDFile"); sleep(1); unless (kill 0, `cat $noVNCPIDFile`) { warn "Could not start noVNC web server.\n\n"; open(LOG, "<$desktopLog"); while () { print; } close(LOG); unlink $noVNCPIDFile; $opt{'-kill'} = ':'.$displayNumber; &Kill(); } $msg = "noVNC URL: "; $msg .= "https://" if ($noVNCEncrypt); $msg .= "http://" if (!$noVNCEncrypt); $msg .= $host . ":" . (5800 + $displayNumber) . "/vnc.html"; $msg .= "?host=" . $host . "&port=" . $vncPort; $msg .= "&encrypt=1" if ($noVNCEncrypt); $msg .= "\n"; warn $msg; } if ($opt{'-sessionstart'}) { &List(0, $displayNumber); } if (!$noxstartup) { # Run the X startup script. warn "Starting applications specified in $xstartup\n"; if ($wm) { $ENV{TVNC_WM} = $wm; } if ($useVGL) { warn "(Enabling VirtualGL)\n"; $ENV{TVNC_VGL} = "1"; } } warn "Log file is $desktopLog\n\n"; # If the unix domain socket exists then use that (DISPLAY=:n) otherwise use # TCP (DISPLAY=host:n) if (-e "/tmp/.X11-unix/X$displayNumber") { $ENV{DISPLAY} = ":$displayNumber"; } else { $ENV{DISPLAY} = "$host:$displayNumber"; } $ENV{VNCDESKTOP} = $desktopName; $ENV{VGL_COMPRESS} = "0"; $ENV{VGL_PROBEGLX} = "0"; if (!$noxstartup) { if ($opt{'-fg'}) { system("$xstartup >> " . "edString($desktopLog) . " 2>&1"); if (kill 0, `cat $pidFile` || kill 0, `cat $noVNCPIDFile`) { $opt{'-kill'} = ':'.$displayNumber; &Kill(); } } else { if ($autokill) { system("($xstartup; $0 -kill :$displayNumber) >> " . "edString($desktopLog) . " 2>&1 &"); } else { system("$xstartup >> " . "edString($desktopLog) . " 2>&1 &"); } } } exit; ############################################################################### # # CheckGeometryAndDepth simply makes sure that the geometry and depth values # are sensible. # sub CheckGeometryAndDepth { foreach $tok (split/[,]/, $geometry) { $width = -1; $height = -1; $x = 0; $y = 0; if ($tok =~ /^(\d+)x(\d+)$/) { $width = $1; $height = $2; } elsif ($tok =~ /^(\d+)x(\d+)\+(\d+)$/) { $width = $1; $height = $2; $x = $3; } elsif ($tok =~ /^(\d+)x(\d+)\+(\d+)\+(\d+)$/) { $width = $1; $height = $2; $x = $3; $y = $4; } if (($width < 1) || ($height < 1)) { die "$prog: geometry $tok is invalid\n"; } } if (($depth < 8) || ($depth > 32)) { die "Depth must be between 8 and 32\n"; } } # # GetDisplayNumber gets the lowest available display number. # sub GetDisplayNumber { foreach $n (1..99) { if (&CheckDisplayNumber($n)) { return $n + 0; # Bruce Mah's workaround for bug in perl 5.005_02 } } die "$prog: no free display number on $host.\n"; } # # CheckDisplayNumber checks if the given display number is available. A # display number $n is taken if any of the following are true: # # - something is listening on the X server port (6000+$n) # - something is listening on the VNC server port (5900+$n) # - -novnc/$noVNC is specified and something is listening on the web server # port (5800+$n) # - (Linux only) the abstract socket \0/tmp/.X11-unix/X$n is in use # - the lock file /tmp/.X$n-lock exists # - the Unix domain socket /tmp/.X11-unix/X$n exists # sub CheckDisplayNumber { local ($n) = @_; socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n"; eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))'; unless (bind(S, pack('S n x12', $AF_INET, 6000 + $n))) { close(S); return 0; } close(S); socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n"; eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))'; unless (bind(S, pack('S n x12', $AF_INET, 5900 + $n))) { close(S); return 0; } close(S); if ($noVNC) { socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n"; eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))'; unless (bind(S, pack('S n x12', $AF_INET, 5800 + $n))) { close(S); return 0; } close(S); } if ($os eq "Linux") { socket(S, $AF_UNIX, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n"; unless (bind(S, pack_sockaddr_un("\0/tmp/.X11-unix/X$n"))) { warn "\nWARNING: $host:$n is taken because abstract socket \\0/tmp/.X11-unix/X$n is in use.\n"; close(S); return 0; } close(S); } if (-e "/tmp/.X$n-lock") { warn "\nWARNING: $host:$n is taken because of /tmp/.X$n-lock\n"; warn "Remove this file if there is no X server $host:$n\n"; return 0; } if (-e "/tmp/.X11-unix/X$n") { warn "\nWARNING: $host:$n is taken because of /tmp/.X11-unix/X$n\n"; warn "Remove this file if there is no X server $host:$n\n"; return 0; } return 1; } # # GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel # format of the current X display being used. If successful, it sets the # options as appropriate so that the TurboVNC X server will use the same settings # (minus an allowance for window manager decorations on the geometry). Using # the same depth and pixel format means that the VNC server won't have to # translate pixels when the desktop is being viewed on this X display (for # TrueColor displays anyway). # sub GetXDisplayDefaults { local (@lines, @matchlines, $width, $height, $defaultVisualId, $i, $red, $green, $blue); $wmDecorationWidth = 4; # a guess at typical size for window manager $wmDecorationHeight = 24; # decoration size return unless (defined($ENV{DISPLAY})); @lines = `xdpyinfo 2>/dev/null`; return if ($? != 0); @matchlines = grep(/dimensions/, @lines); if (@matchlines) { ($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/); $width -= $wmDecorationWidth; $height -= $wmDecorationHeight; $geometry = "${width}x$height"; } @matchlines = grep(/default visual id/, @lines); if (@matchlines) { ($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/); for ($i = 0; $i < @lines; $i++) { if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) { if (($lines[$i + 1] !~ /TrueColor/) || ($lines[$i + 2] !~ /depth/) || ($lines[$i + 4] !~ /red, green, blue masks/)) { return; } last; } } return if ($i >= @lines); ($depth) = ($lines[$i + 2] =~ /depth:\s+(\d+)/); ($red, $green, $blue) = ($lines[$i + 4] =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/); $red = hex($red); $green = hex($green); $blue = hex($blue); if ($red > $blue) { $red = int(log($red) / log(2)) - int(log($green) / log(2)); $green = int(log($green) / log(2)) - int(log($blue) / log(2)); $blue = int(log($blue) / log(2)) + 1; $pixelformat = "rgb$red$green$blue"; } else { $blue = int(log($blue) / log(2)) - int(log($green) / log(2)); $green = int(log($green) / log(2)) - int(log($red) / log(2)); $red = int(log($red) / log(2)) + 1; $pixelformat = "bgr$blue$green$red"; } } } # # quotedString returns a string which yields the original string when parsed # by a shell. # sub quotedString { local ($in) = @_; $in =~ s/\'/\'\"\'\"\'/g; return "'$in'"; } # # removeSlashes turns slashes into underscores for use as a file name. # sub removeSlashes { local ($in) = @_; $in =~ s|/|_|g; return "$in"; } # # Usage # sub Usage { die("TurboVNC Server v3.1.1 (build 20240127)\n". "\n". "Usage: $prog [] [:]\n". " $prog -kill :\n". " $prog -list\n". "\n". " are Xvnc options, or:\n". "\n". " -geometry x or\n". " -geometry x++[,x

++,...]\n". " -depth \n". " -pixelformat rgb\n". " -pixelformat bgr\n". " -fp \n". " -name \n". " -novnc \n". " -otp\n". " -fg\n". " -x509cert \n". " -x509key \n". " -noautokill\n". " -noxstartup\n". " -xstartup Githubissues.
  • Githubissues is a development platform for aggregating issues.