blob: 2ef436a7a47b229c585503c2bf9468eed8c314e6 [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001#!/usr/bin/env perl
2#
DRCb9d8e762011-02-09 08:24:58 +00003# Copyright (C) 2009-2010 D. R. Commander. All Rights Reserved.
DRC190854c2009-03-26 18:13:00 +00004# Copyright (C) 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
5# Copyright (C) 2002-2003 Constantin Kaplinsky. All Rights Reserved.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00006# Copyright (C) 2002-2005 RealVNC Ltd.
7# Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
8#
9# This is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This software is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this software; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22# USA.
23#
24
25#
26# vncserver - wrapper script to start an X VNC server.
27#
28
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000029# First make sure we're operating in a sane environment.
DRC190854c2009-03-26 18:13:00 +000030$exedir = "";
31$slashndx = rindex($0, "/");
32if($slashndx>=0) {
33 $exedir = substr($0, 0, $slashndx+1);
34}
35
36$vncClasses = "";
37
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000038&SanityCheck();
39
40#
Jeff Blaine8a9abc12016-08-06 16:22:18 -040041# Global variables. You may want to configure some of these for
42# your site
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000043#
44
45$geometry = "1024x768";
DRC9d1c1572009-03-26 18:18:51 +000046#$depth = 16;
DRCe85eba52011-10-04 06:57:19 +000047$vncJavaFiles = (((-d "$vncClasses") && "$vncClasses") ||
48 ((-d "/usr/share/vnc/classes") && "/usr/share/vnc/classes") ||
49 ((-d "/usr/local/vnc/classes") && "/usr/local/vnc/classes"));
Koichiro IWAO977fbde2016-10-03 18:07:57 +090050
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000051$vncUserDir = "$ENV{HOME}/.vnc";
Jeff Blaine8a9abc12016-08-06 16:22:18 -040052$vncUserConfig = "$vncUserDir/config";
53
54$vncSystemConfigDir = "/etc/tigervnc";
55$vncSystemConfigDefaultsFile = "$vncSystemConfigDir/vncserver-config-defaults";
56$vncSystemConfigMandatoryFile = "$vncSystemConfigDir/vncserver-config-mandatory";
57
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +020058$skipxstartup = 0;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000059$xauthorityFile = "$ENV{XAUTHORITY}" || "$ENV{HOME}/.Xauthority";
60
Jeff Blaine8a9abc12016-08-06 16:22:18 -040061$xstartupFile = $vncUserDir . "/xstartup";
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000062$defaultXStartup
63 = ("#!/bin/sh\n\n".
DRC93248982009-03-26 18:14:38 +000064 "unset SESSION_MANAGER\n".
Adam Tkacc071e492009-05-20 09:01:24 +000065 "unset DBUS_SESSION_BUS_ADDRESS\n".
DRC93248982009-03-26 18:14:38 +000066 "OS=`uname -s`\n".
67 "if [ \$OS = 'Linux' ]; then\n".
68 " case \"\$WINDOWMANAGER\" in\n".
69 " \*gnome\*)\n".
70 " if [ -e /etc/SuSE-release ]; then\n".
71 " PATH=\$PATH:/opt/gnome/bin\n".
72 " export PATH\n".
73 " fi\n".
74 " ;;\n".
75 " esac\n".
76 "fi\n".
77 "if [ -x /etc/X11/xinit/xinitrc ]; then\n".
78 " exec /etc/X11/xinit/xinitrc\n".
79 "fi\n".
80 "if [ -f /etc/X11/xinit/xinitrc ]; then\n".
81 " exec sh /etc/X11/xinit/xinitrc\n".
82 "fi\n".
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000083 "[ -r \$HOME/.Xresources ] && xrdb \$HOME/.Xresources\n".
84 "xsetroot -solid grey\n".
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000085 "xterm -geometry 80x24+10+10 -ls -title \"\$VNCDESKTOP Desktop\" &\n".
86 "twm &\n");
87
grayskyddff8d02015-10-19 08:24:14 -040088$defaultConfig
89 = ("## Supported server options to pass to vncserver upon invocation can be listed\n".
90 "## in this file. See the following manpages for more: vncserver(1) Xvnc(1).\n".
91 "## Several common ones are shown below. Uncomment and modify to your liking.\n".
92 "##\n".
93 "# securitytypes=vncauth,tlsvnc\n".
94 "# desktop=sandbox\n".
95 "# geometry=2000x1200\n".
96 "# localhost\n".
97 "# alwaysshared\n");
98
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000099chop($host = `uname -n`);
100
DRC989dbd12009-04-22 13:23:17 +0000101if (-d "/etc/X11/fontpath.d") {
102 $fontPath = "catalogue:/etc/X11/fontpath.d";
103}
DRC36546c12009-04-15 06:47:23 +0000104
DRCd6821bf2009-03-26 18:17:49 +0000105@fontpaths = ('/usr/share/X11/fonts', '/usr/share/fonts', '/usr/share/fonts/X11/');
106if (! -l "/usr/lib/X11") {push(@fontpaths, '/usr/lib/X11/fonts');}
107if (! -l "/usr/X11") {push(@fontpaths, '/usr/X11/lib/X11/fonts');}
108if (! -l "/usr/X11R6") {push(@fontpaths, '/usr/X11R6/lib/X11/fonts');}
109push(@fontpaths, '/usr/share/fonts/default');
110
111@fonttypes = ('misc',
112 '75dpi',
113 '100dpi',
114 'Speedo',
115 'Type1');
116
117foreach $_fpath (@fontpaths) {
118 foreach $_ftype (@fonttypes) {
119 if (-f "$_fpath/$_ftype/fonts.dir") {
120 if (! -l "$_fpath/$_ftype") {
DRC36546c12009-04-15 06:47:23 +0000121 $defFontPath .= "$_fpath/$_ftype,";
DRCd6821bf2009-03-26 18:17:49 +0000122 }
123 }
124 }
125}
DRCd28792b2010-01-11 20:53:00 +0000126
DRC36546c12009-04-15 06:47:23 +0000127if ($defFontPath) {
128 if (substr($defFontPath, -1, 1) == ',') {
129 chop $defFontPath;
DRCd6821bf2009-03-26 18:17:49 +0000130 }
131}
132
DRCd28792b2010-01-11 20:53:00 +0000133if ($fontPath eq "") {
134 $fontPath = $defFontPath;
135}
136
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000137# Check command line options
138
139&ParseOptions("-geometry",1,"-depth",1,"-pixelformat",1,"-name",1,"-kill",1,
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100140 "-help",0,"-h",0,"--help",0,"-fp",1,"-list",0,"-fg",0,"-autokill",0,"-noxstartup",0,"-xstartup",1);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000141
142&Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'});
143
144&Kill() if ($opt{'-kill'});
145
DRCb9d8e762011-02-09 08:24:58 +0000146&List() if ($opt{'-list'});
147
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000148# Uncomment this line if you want default geometry, depth and pixelformat
149# to match the current X display:
150# &GetXDisplayDefaults();
151
152if ($opt{'-geometry'}) {
153 $geometry = $opt{'-geometry'};
154}
155if ($opt{'-depth'}) {
156 $depth = $opt{'-depth'};
157 $pixelformat = "";
158}
159if ($opt{'-pixelformat'}) {
160 $pixelformat = $opt{'-pixelformat'};
161}
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200162if ($opt{'-noxstartup'}) {
163 $skipxstartup = 1;
164}
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100165if ($opt{'-xstartup'}) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400166 $xstartupFile = $opt{'-xstartup'};
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100167}
DRCeed5d1f2009-03-26 19:16:19 +0000168if ($opt{'-fp'}) {
169 $fontPath = $opt{'-fp'};
DRC36546c12009-04-15 06:47:23 +0000170 $fpArgSpecified = 1;
DRCeed5d1f2009-03-26 19:16:19 +0000171}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000172
173&CheckGeometryAndDepth();
174
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000175# Create the user's vnc directory if necessary.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000176if (!(-e $vncUserDir)) {
177 if (!mkdir($vncUserDir,0755)) {
178 die "$prog: Could not create $vncUserDir.\n";
179 }
180}
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900181
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400182# Find display number.
183if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) {
184 $displayNumber = $1;
185 shift(@ARGV);
186 if (!&CheckDisplayNumber($displayNumber)) {
187 die "A VNC server is already running as :$displayNumber\n";
188 }
189} elsif ((@ARGV > 0) && ($ARGV[0] !~ /^-/) && ($ARGV[0] !~ /^\+/)) {
190 &Usage();
191} else {
192 $displayNumber = &GetDisplayNumber();
193}
194
195$vncPort = 5900 + $displayNumber;
196
197if ($opt{'-name'}) {
198 $desktopName = $opt{'-name'};
199} else {
200 $desktopName = "$host:$displayNumber ($ENV{USER})";
201}
202
203my %default_opts;
204my %config;
205
206# We set some reasonable defaults. Config file settings
207# override these where present.
208$default_opts{desktop} = &quotedString($desktopName);
209$default_opts{httpd} = $vncJavaFiles if ($vncJavaFiles);
210$default_opts{auth} = $xauthorityFile;
211$default_opts{geometry} = $geometry if ($geometry);
212$default_opts{depth} = $depth if ($depth);
213$default_opts{pixelformat} = $pixelformat if ($pixelformat);
214$default_opts{rfbwait} = 30000;
215$default_opts{rfbauth} = "$vncUserDir/passwd";
216$default_opts{rfbport} = $vncPort;
217$default_opts{fp} = $fontPath if ($fontPath);
218$default_opts{pn} = "";
219
220# Load user-overrideable system defaults
221LoadConfig($vncSystemConfigDefaultsFile);
222
223# Then the user's settings
224LoadConfig($vncUserConfig);
225
226# And then override anything set above if mandatory settings exist.
227# WARNING: "Mandatory" is used loosely here! As the man page says,
228# there is nothing stopping someone from EASILY subverting the
229# settings in $vncSystemConfigMandatoryFile by simply passing
230# CLI args to vncserver, which trump config files! To properly
231# hard force policy in a non-subvertible way would require major
232# development work that touches Xvnc itself.
233LoadConfig($vncSystemConfigMandatoryFile, 1);
234
235#
Adam Tkacf586b842011-04-27 11:20:18 +0000236# Check whether VNC authentication is enabled, and if so, prompt the user to
237# create a VNC password if they don't already have one.
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400238#
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000239
Adam Tkacf586b842011-04-27 11:20:18 +0000240$securityTypeArgSpecified = 0;
241$vncAuthEnabled = 0;
242$passwordArgSpecified = 0;
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400243@vncAuthStrings = ("vncauth", "tlsvnc", "x509vnc");
Adam Tkacf586b842011-04-27 11:20:18 +0000244
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400245# ...first we check our configuration files' settings
246if ($config{'securitytypes'}) {
247 $securityTypeArgSpecified = 1;
248 foreach $arg2 (split(',', $config{'securitytypes'})) {
249 if (grep {$_ eq lc($arg2)} @vncAuthStrings) {
250 $vncAuthEnabled = 1;
251 }
252 }
253}
254
255# ...and finally we check CLI args, which in the case of the topic at
256# hand (VNC auth or not), override anything found in configuration files
257# (even so-called "mandatory" settings).
Adam Tkacf586b842011-04-27 11:20:18 +0000258for ($i = 0; $i < @ARGV; ++$i) {
259 # -SecurityTypes can be followed by a space or "="
260 my @splitargs = split('=', $ARGV[$i]);
261 if (@splitargs <= 1 && $i < @ARGV - 1) {
262 push(@splitargs, $ARGV[$i + 1]);
263 }
264 if (lc(@splitargs[0]) eq "-securitytypes") {
265 if (@splitargs > 1) {
266 $securityTypeArgSpecified = 1;
267 }
268 foreach $arg2 (split(',', @splitargs[1])) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400269 if (grep {$_ eq lc($arg2)} @vncAuthStrings) {
Adam Tkacf586b842011-04-27 11:20:18 +0000270 $vncAuthEnabled = 1;
271 }
272 }
273 }
274 if ((lc(@splitargs[0]) eq "-password")
275 || (lc(@splitargs[0]) eq "-passwordfile"
276 || (lc(@splitargs[0]) eq "-rfbauth"))) {
277 $passwordArgSpecified = 1;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000278 }
279}
280
Adam Tkacf586b842011-04-27 11:20:18 +0000281if ((!$securityTypeArgSpecified || $vncAuthEnabled) && !$passwordArgSpecified) {
282 ($z,$z,$mode) = stat("$vncUserDir/passwd");
283 if (!(-e "$vncUserDir/passwd") || ($mode & 077)) {
284 warn "\nYou will require a password to access your desktops.\n\n";
285 system($exedir."vncpasswd -q $vncUserDir/passwd");
286 if (($? >> 8) != 0) {
287 exit 1;
288 }
289 }
290}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000291
292$desktopLog = "$vncUserDir/$host:$displayNumber.log";
293unlink($desktopLog);
294
Pierre Ossman5fe60702015-12-29 14:27:07 +0100295# Make an X server cookie and set up the Xauthority file
Koichiro IWAO31cad942016-10-04 17:17:06 +0900296# mcookie is a part of util-linux, usually only GNU/Linux systems have it.
Pierre Ossman5fe60702015-12-29 14:27:07 +0100297$cookie = `mcookie`;
Koichiro IWAO31cad942016-10-04 17:17:06 +0900298# Fallback for non GNU/Linux OS - use /dev/urandom on systems that have it,
299# otherwise use perl's random number generator, seeded with the sum
300# of the current time, our PID and part of the encrypted form of the password.
301if ($cookie eq "" && open(URANDOM, '<', '/dev/urandom')) {
302 my $randata;
303 if (sysread(URANDOM, $randata, 16) == 16) {
304 $cookie = unpack 'h*', $randata;
305 }
306 close(URANDOM);
307}
308if ($cookie eq "") {
309 srand(time+$$+unpack("L",`cat $vncUserDir/passwd`));
310 for (1..16) {
311 $cookie .= sprintf("%02x", int(rand(256)) % 256);
312 }
313}
Adam Tkac6cbd9d12009-11-12 10:39:54 +0000314
Pierre Ossmancddd6482015-12-29 14:30:32 +0100315open(XAUTH, "|xauth -f $xauthorityFile source -");
316print XAUTH "add $host:$displayNumber . $cookie\n";
317print XAUTH "add $host/unix:$displayNumber . $cookie\n";
318close(XAUTH);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000319
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000320# Now start the X VNC Server
321
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400322# We build up our Xvnc command with options
DRC190854c2009-03-26 18:13:00 +0000323$cmd = $exedir."Xvnc :$displayNumber";
grayskyddff8d02015-10-19 08:24:14 -0400324
grayskyddff8d02015-10-19 08:24:14 -0400325foreach my $k (sort keys %config) {
326 $cmd .= " -$k $config{$k}";
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400327 delete $default_opts{$k}; # file options take precedence
grayskyddff8d02015-10-19 08:24:14 -0400328}
329
330foreach my $k (sort keys %default_opts) {
331 $cmd .= " -$k $default_opts{$k}";
332}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000333
DRCd6821bf2009-03-26 18:17:49 +0000334# Add color database stuff here, e.g.:
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000335# $cmd .= " -co /usr/lib/X11/rgb";
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000336
337foreach $arg (@ARGV) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400338 $cmd .= " " . &quotedString($arg);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000339}
340$cmd .= " >> " . &quotedString($desktopLog) . " 2>&1";
341
342# Run $cmd and record the process ID.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000343$pidFile = "$vncUserDir/$host:$displayNumber.pid";
344system("$cmd & echo \$! >$pidFile");
345
346# Give Xvnc a chance to start up
347
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900348sleep(3);
DRCd28792b2010-01-11 20:53:00 +0000349if ($fontPath ne $defFontPath) {
350 unless (kill 0, `cat $pidFile`) {
351 if ($fpArgSpecified) {
352 warn "\nWARNING: The first attempt to start Xvnc failed, probably because the font\n";
353 warn "path you specified using the -fp argument is incorrect. Attempting to\n";
354 warn "determine an appropriate font path for this system and restart Xvnc using\n";
355 warn "that font path ...\n";
356 } else {
357 warn "\nWARNING: The first attempt to start Xvnc failed, possibly because the font\n";
358 warn "catalog is not properly configured. Attempting to determine an appropriate\n";
359 warn "font path for this system and restart Xvnc using that font path ...\n";
360 }
361 $cmd =~ s@-fp [^ ]+@@;
362 $cmd .= " -fp $defFontPath" if ($defFontPath);
363 system("$cmd & echo \$! >$pidFile");
364 sleep(3);
DRC36546c12009-04-15 06:47:23 +0000365 }
DRCd6821bf2009-03-26 18:17:49 +0000366}
367unless (kill 0, `cat $pidFile`) {
368 warn "Could not start Xvnc.\n\n";
Michal Srbe6e11f92015-10-02 02:28:26 +0300369 unlink $pidFile;
DRCd6821bf2009-03-26 18:17:49 +0000370 open(LOG, "<$desktopLog");
371 while (<LOG>) { print; }
372 close(LOG);
373 die "\n";
374}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000375
376warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
377
378# Create the user's xstartup script if necessary.
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200379if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400380 if (!(-e "$xstartupFile")) {
381 warn "Creating default startup script $xstartupFile\n";
382 open(XSTARTUP, ">$xstartupFile");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200383 print XSTARTUP $defaultXStartup;
384 close(XSTARTUP);
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400385 chmod 0755, "$xstartupFile";
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200386 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000387}
388
grayskyddff8d02015-10-19 08:24:14 -0400389# Create the user's config file if necessary.
grayskyddff8d02015-10-19 08:24:14 -0400390if (!(-e "$vncUserDir/config")) {
391 warn "Creating default config $vncUserDir/config\n";
Jeff Blained6777a62016-05-23 21:43:59 -0400392 open(VNCUSERCONFIG, ">$vncUserDir/config");
393 print VNCUSERCONFIG $defaultConfig;
394 close(VNCUSERCONFIG);
grayskyddff8d02015-10-19 08:24:14 -0400395 chmod 0644, "$vncUserDir/config";
396}
397
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000398# Run the X startup script.
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200399if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400400 warn "Starting applications specified in $xstartupFile\n";
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200401}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000402warn "Log file is $desktopLog\n\n";
403
404# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
405# TCP (DISPLAY=host:n)
406
407if (-e "/tmp/.X11-unix/X$displayNumber" ||
408 -e "/usr/spool/sockets/X11/$displayNumber")
409{
410 $ENV{DISPLAY}= ":$displayNumber";
411} else {
412 $ENV{DISPLAY}= "$host:$displayNumber";
413}
414$ENV{VNCDESKTOP}= $desktopName;
415
DRC8fb11912011-03-03 10:42:14 +0000416if ($opt{'-fg'}) {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200417 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400418 system("$xstartupFile >> " . &quotedString($desktopLog) . " 2>&1");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200419 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000420 if (kill 0, `cat $pidFile`) {
421 $opt{'-kill'} = ':'.$displayNumber;
422 &Kill();
423 }
DRC8fb11912011-03-03 10:42:14 +0000424} else {
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000425 if ($opt{'-autokill'}) {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200426 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400427 system("($xstartupFile; $0 -kill :$displayNumber) >> "
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100428 . &quotedString($desktopLog) . " 2>&1 &");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200429 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000430 } else {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200431 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400432 system("$xstartupFile >> " . &quotedString($desktopLog)
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100433 . " 2>&1 &");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200434 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000435 }
DRC8fb11912011-03-03 10:42:14 +0000436}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000437
438exit;
439
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000440###############################################################################
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400441# Functions
442###############################################################################
443
444#
445# Populate the global %config hash with settings from a specified
446# vncserver configuration file if it exists
447#
448# Args: 1. file path
449# 2. optional boolean flag to enable warning when a previously
450# set configuration setting is being overridden
451#
452sub LoadConfig {
453 local ($configFile, $warnoverride) = @_;
454 local ($toggle) = undef;
455
456 if (stat($configFile)) {
457 if (open(IN, $configFile)) {
458 while (<IN>) {
459 next if /^#/;
460 if (my ($k, $v) = /^\s*(\w+)\s*=\s*(.+)$/) {
461 $k = lc($k); # must normalize key case
462 if ($warnoverride && $config{$k}) {
463 print("Warning: $configFile is overriding previously defined '$k' to be '$v'\n");
464 }
465 $config{$k} = $v;
466 } elsif ($_ =~ m/^\s*(\S+)/) {
467 # We can't reasonably warn on override of toggles (e.g. AlwaysShared)
468 # because it would get crazy to do so. We'd have to check if the
469 # current config file being loaded defined the logical opposite setting
470 # (NeverShared vs. AlwaysShared, etc etc).
471 $toggle = lc($1); # must normalize key case
472 $config{$toggle} = $k;
473 }
474 }
475 close(IN);
476 }
477 }
478}
479
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000480#
481# CheckGeometryAndDepth simply makes sure that the geometry and depth values
482# are sensible.
483#
484
485sub CheckGeometryAndDepth
486{
487 if ($geometry =~ /^(\d+)x(\d+)$/) {
488 $width = $1; $height = $2;
489
490 if (($width<1) || ($height<1)) {
491 die "$prog: geometry $geometry is invalid\n";
492 }
493
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000494 $geometry = "${width}x$height";
495 } else {
496 die "$prog: geometry $geometry is invalid\n";
497 }
498
DRCe5b4f752009-03-26 18:23:29 +0000499 if ($depth && (($depth < 8) || ($depth > 32))) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000500 die "Depth must be between 8 and 32\n";
501 }
502}
503
504
505#
506# GetDisplayNumber gets the lowest available display number. A display number
507# n is taken if something is listening on the VNC server port (5900+n) or the
508# X server port (6000+n).
509#
510
511sub GetDisplayNumber
512{
513 foreach $n (1..99) {
514 if (&CheckDisplayNumber($n)) {
515 return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
516 }
517 }
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900518
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000519 die "$prog: no free display number on $host.\n";
520}
521
522
523#
524# CheckDisplayNumber checks if the given display number is available. A
525# display number n is taken if something is listening on the VNC server port
526# (5900+n) or the X server port (6000+n).
527#
528
529sub CheckDisplayNumber
530{
531 local ($n) = @_;
532
533 socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
534 eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
535 if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
536 close(S);
537 return 0;
538 }
539 close(S);
540
541 socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
542 eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
543 if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
544 close(S);
545 return 0;
546 }
547 close(S);
548
549 if (-e "/tmp/.X$n-lock") {
550 warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
551 warn "Remove this file if there is no X server $host:$n\n";
552 return 0;
553 }
554
555 if (-e "/tmp/.X11-unix/X$n") {
556 warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
557 warn "Remove this file if there is no X server $host:$n\n";
558 return 0;
559 }
560
561 if (-e "/usr/spool/sockets/X11/$n") {
562 warn("\nWarning: $host:$n is taken because of ".
563 "/usr/spool/sockets/X11/$n\n");
564 warn "Remove this file if there is no X server $host:$n\n";
565 return 0;
566 }
567
568 return 1;
569}
570
571
572#
573# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
574# format of the current X display being used. If successful, it sets the
575# options as appropriate so that the X VNC server will use the same settings
576# (minus an allowance for window manager decorations on the geometry). Using
577# the same depth and pixel format means that the VNC server won't have to
578# translate pixels when the desktop is being viewed on this X display (for
579# TrueColor displays anyway).
580#
581
582sub GetXDisplayDefaults
583{
584 local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
585 $red, $green, $blue);
586
587 $wmDecorationWidth = 4; # a guess at typical size for window manager
588 $wmDecorationHeight = 24; # decoration size
589
590 return if (!defined($ENV{DISPLAY}));
591
592 @lines = `xdpyinfo 2>/dev/null`;
593
594 return if ($? != 0);
595
596 @matchlines = grep(/dimensions/, @lines);
597 if (@matchlines) {
598 ($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
599
600 $width -= $wmDecorationWidth;
601 $height -= $wmDecorationHeight;
602
603 $geometry = "${width}x$height";
604 }
605
606 @matchlines = grep(/default visual id/, @lines);
607 if (@matchlines) {
608 ($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
609
610 for ($i = 0; $i < @lines; $i++) {
611 if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
612 if (($lines[$i+1] !~ /TrueColor/) ||
613 ($lines[$i+2] !~ /depth/) ||
614 ($lines[$i+4] !~ /red, green, blue masks/))
615 {
616 return;
617 }
618 last;
619 }
620 }
621
622 return if ($i >= @lines);
623
624 ($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
625 ($red,$green,$blue)
626 = ($lines[$i+4]
627 =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
628
629 $red = hex($red);
630 $green = hex($green);
631 $blue = hex($blue);
632
633 if ($red > $blue) {
634 $red = int(log($red) / log(2)) - int(log($green) / log(2));
635 $green = int(log($green) / log(2)) - int(log($blue) / log(2));
636 $blue = int(log($blue) / log(2)) + 1;
637 $pixelformat = "rgb$red$green$blue";
638 } else {
639 $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
640 $green = int(log($green) / log(2)) - int(log($red) / log(2));
641 $red = int(log($red) / log(2)) + 1;
642 $pixelformat = "bgr$blue$green$red";
643 }
644 }
645}
646
647
648#
649# quotedString returns a string which yields the original string when parsed
650# by a shell.
651#
652
653sub quotedString
654{
655 local ($in) = @_;
656
657 $in =~ s/\'/\'\"\'\"\'/g;
658
659 return "'$in'";
660}
661
662
663#
664# removeSlashes turns slashes into underscores for use as a file name.
665#
666
667sub removeSlashes
668{
669 local ($in) = @_;
670
671 $in =~ s|/|_|g;
672
673 return "$in";
674}
675
676
677#
678# Usage
679#
680
681sub Usage
682{
683 die("\nusage: $prog [:<number>] [-name <desktop-name>] [-depth <depth>]\n".
684 " [-geometry <width>x<height>]\n".
685 " [-pixelformat rgbNNN|bgrNNN]\n".
DRCeed5d1f2009-03-26 19:16:19 +0000686 " [-fp <font-path>]\n".
DRC8fb11912011-03-03 10:42:14 +0000687 " [-fg]\n".
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000688 " [-autokill]\n".
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200689 " [-noxstartup]\n".
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100690 " [-xstartup <file>]\n".
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000691 " <Xvnc-options>...\n\n".
DRCb9d8e762011-02-09 08:24:58 +0000692 " $prog -kill <X-display>\n\n".
693 " $prog -list\n\n");
694}
695
696
697#
698# List
699#
700
701sub List
702{
703 opendir(dir, $vncUserDir);
704 my @filelist = readdir(dir);
705 closedir(dir);
DRC075d9fa2011-02-10 04:19:46 +0000706 print "\nTigerVNC server sessions:\n\n";
DRCb9d8e762011-02-09 08:24:58 +0000707 print "X DISPLAY #\tPROCESS ID\n";
708 foreach my $file (@filelist) {
709 if ($file =~ /$host:(\d+)$\.pid/) {
Michal Srbe6e11f92015-10-02 02:28:26 +0300710 chop($tmp_pid = `cat $vncUserDir/$file`);
711 if (kill 0, $tmp_pid) {
712 print ":".$1."\t\t".`cat $vncUserDir/$file`;
713 } else {
714 unlink ($vncUserDir . "/" . $file);
715 }
DRCb9d8e762011-02-09 08:24:58 +0000716 }
717 }
718 exit 1;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000719}
720
721
722#
723# Kill
724#
725
726sub Kill
727{
728 $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
729
730 if ($opt{'-kill'} =~ /^:\d+$/) {
731 $pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
732 } else {
733 if ($opt{'-kill'} !~ /^$host:/) {
734 die "\nCan't tell if $opt{'-kill'} is on $host\n".
735 "Use -kill :<number> instead\n\n";
736 }
737 $pidFile = "$vncUserDir/$opt{'-kill'}.pid";
738 }
739
740 if (! -r $pidFile) {
741 die "\nCan't find file $pidFile\n".
742 "You'll have to kill the Xvnc process manually\n\n";
743 }
744
745 $SIG{'HUP'} = 'IGNORE';
746 chop($pid = `cat $pidFile`);
747 warn "Killing Xvnc process ID $pid\n";
DRCb9d8e762011-02-09 08:24:58 +0000748
749 if (kill 0, $pid) {
750 system("kill $pid");
751 sleep(1);
752 if (kill 0, $pid) {
753 print "Xvnc seems to be deadlocked. Kill the process manually and then re-run\n";
754 print " ".$0." -kill ".$opt{'-kill'}."\n";
755 print "to clean up the socket files.\n";
756 exit
757 }
758
759 } else {
760 warn "Xvnc process ID $pid already killed\n";
761 $opt{'-kill'} =~ s/://;
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900762
DRCb9d8e762011-02-09 08:24:58 +0000763 if (-e "/tmp/.X11-unix/X$opt{'-kill'}") {
764 print "Xvnc did not appear to shut down cleanly.";
765 print " Removing /tmp/.X11-unix/X$opt{'-kill'}\n";
766 unlink "/tmp/.X11-unix/X$opt{'-kill'}";
767 }
768 if (-e "/tmp/.X$opt{'-kill'}-lock") {
769 print "Xvnc did not appear to shut down cleanly.";
770 print " Removing /tmp/.X$opt{'-kill'}-lock\n";
771 unlink "/tmp/.X$opt{'-kill'}-lock";
772 }
773 }
774
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000775 unlink $pidFile;
776 exit;
777}
778
779
780#
781# ParseOptions takes a list of possible options and a boolean indicating
782# whether the option has a value following, and sets up an associative array
783# %opt of the values of the options given on the command line. It removes all
784# the arguments it uses from @ARGV and returns them in @optArgs.
785#
786
787sub ParseOptions
788{
789 local (@optval) = @_;
790 local ($opt, @opts, %valFollows, @newargs);
791
792 while (@optval) {
793 $opt = shift(@optval);
794 push(@opts,$opt);
795 $valFollows{$opt} = shift(@optval);
796 }
797
798 @optArgs = ();
799 %opt = ();
800
801 arg: while (defined($arg = shift(@ARGV))) {
802 foreach $opt (@opts) {
803 if ($arg eq $opt) {
804 push(@optArgs, $arg);
805 if ($valFollows{$opt}) {
806 if (@ARGV == 0) {
807 &Usage();
808 }
809 $opt{$opt} = shift(@ARGV);
810 push(@optArgs, $opt{$opt});
811 } else {
812 $opt{$opt} = 1;
813 }
814 next arg;
815 }
816 }
817 push(@newargs,$arg);
818 }
819
820 @ARGV = @newargs;
821}
822
823
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000824# Routine to make sure we're operating in a sane environment.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000825sub SanityCheck
826{
827 local ($cmd);
828
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000829 # Get the program name
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000830 ($prog) = ($0 =~ m|([^/]+)$|);
831
832 #
833 # Check we have all the commands we'll need on the path.
834 #
835
836 cmd:
Koichiro IWAO31cad942016-10-04 17:17:06 +0900837 foreach $cmd ("uname","xauth") {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000838 for (split(/:/,$ENV{PATH})) {
839 if (-x "$_/$cmd") {
840 next cmd;
841 }
842 }
843 die "$prog: couldn't find \"$cmd\" on your PATH.\n";
844 }
DRC190854c2009-03-26 18:13:00 +0000845
846 if($exedir eq "") {
847 cmd2:
848 foreach $cmd ("Xvnc","vncpasswd") {
849 for (split(/:/,$ENV{PATH})) {
850 if (-x "$_/$cmd") {
851 $vncClasses = "$_/../vnc/classes";
852 next cmd2;
853 }
854 }
855 die "$prog: couldn't find \"$cmd\" on your PATH.\n";
856 }
857 }
858 else {
859 cmd3:
860 foreach $cmd ($exedir."Xvnc",$exedir."vncpasswd") {
861 for (split(/:/,$ENV{PATH})) {
862 if (-x "$cmd") {
863 $vncClasses = $exedir."../vnc/classes";
864 next cmd3;
865 }
866 }
867 die "$prog: couldn't find \"$cmd\".\n";
868 }
869 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000870
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000871 if (!defined($ENV{HOME})) {
872 die "$prog: The HOME environment variable is not set.\n";
873 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000874
875 #
876 # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
877 # eval, and if it fails we try 'require "sys/socket.ph"'. If this fails,
878 # we just guess at the values. If you find perl moaning here, just
879 # hard-code the values of AF_INET and SOCK_STREAM. You can find these out
880 # for your platform by looking in /usr/include/sys/socket.h and related
881 # files.
882 #
883
884 chop($os = `uname`);
885 chop($osrev = `uname -r`);
886
887 eval 'use Socket';
888 if ($@) {
889 eval 'require "sys/socket.ph"';
890 if ($@) {
891 if (($os eq "SunOS") && ($osrev !~ /^4/)) {
892 $AF_INET = 2;
893 $SOCK_STREAM = 2;
894 } else {
895 $AF_INET = 2;
896 $SOCK_STREAM = 1;
897 }
898 } else {
899 $AF_INET = &AF_INET;
900 $SOCK_STREAM = &SOCK_STREAM;
901 }
902 } else {
903 $AF_INET = &AF_INET;
904 $SOCK_STREAM = &SOCK_STREAM;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000905 }
906}