blob: f12df578c76df506cbc8c35a055404d721d60885 [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"));
50
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}
181
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
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000296
Pierre Ossman5fe60702015-12-29 14:27:07 +0100297$cookie = `mcookie`;
Adam Tkac6cbd9d12009-11-12 10:39:54 +0000298
Pierre Ossmancddd6482015-12-29 14:30:32 +0100299open(XAUTH, "|xauth -f $xauthorityFile source -");
300print XAUTH "add $host:$displayNumber . $cookie\n";
301print XAUTH "add $host/unix:$displayNumber . $cookie\n";
302close(XAUTH);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000303
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000304# Now start the X VNC Server
305
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400306# We build up our Xvnc command with options
DRC190854c2009-03-26 18:13:00 +0000307$cmd = $exedir."Xvnc :$displayNumber";
grayskyddff8d02015-10-19 08:24:14 -0400308
grayskyddff8d02015-10-19 08:24:14 -0400309foreach my $k (sort keys %config) {
310 $cmd .= " -$k $config{$k}";
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400311 delete $default_opts{$k}; # file options take precedence
grayskyddff8d02015-10-19 08:24:14 -0400312}
313
314foreach my $k (sort keys %default_opts) {
315 $cmd .= " -$k $default_opts{$k}";
316}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000317
DRCd6821bf2009-03-26 18:17:49 +0000318# Add color database stuff here, e.g.:
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000319# $cmd .= " -co /usr/lib/X11/rgb";
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000320
321foreach $arg (@ARGV) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400322 $cmd .= " " . &quotedString($arg);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000323}
324$cmd .= " >> " . &quotedString($desktopLog) . " 2>&1";
325
326# Run $cmd and record the process ID.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000327$pidFile = "$vncUserDir/$host:$displayNumber.pid";
328system("$cmd & echo \$! >$pidFile");
329
330# Give Xvnc a chance to start up
331
332sleep(3);
DRCd28792b2010-01-11 20:53:00 +0000333if ($fontPath ne $defFontPath) {
334 unless (kill 0, `cat $pidFile`) {
335 if ($fpArgSpecified) {
336 warn "\nWARNING: The first attempt to start Xvnc failed, probably because the font\n";
337 warn "path you specified using the -fp argument is incorrect. Attempting to\n";
338 warn "determine an appropriate font path for this system and restart Xvnc using\n";
339 warn "that font path ...\n";
340 } else {
341 warn "\nWARNING: The first attempt to start Xvnc failed, possibly because the font\n";
342 warn "catalog is not properly configured. Attempting to determine an appropriate\n";
343 warn "font path for this system and restart Xvnc using that font path ...\n";
344 }
345 $cmd =~ s@-fp [^ ]+@@;
346 $cmd .= " -fp $defFontPath" if ($defFontPath);
347 system("$cmd & echo \$! >$pidFile");
348 sleep(3);
DRC36546c12009-04-15 06:47:23 +0000349 }
DRCd6821bf2009-03-26 18:17:49 +0000350}
351unless (kill 0, `cat $pidFile`) {
352 warn "Could not start Xvnc.\n\n";
Michal Srbe6e11f92015-10-02 02:28:26 +0300353 unlink $pidFile;
DRCd6821bf2009-03-26 18:17:49 +0000354 open(LOG, "<$desktopLog");
355 while (<LOG>) { print; }
356 close(LOG);
357 die "\n";
358}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000359
360warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
361
362# Create the user's xstartup script if necessary.
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200363if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400364 if (!(-e "$xstartupFile")) {
365 warn "Creating default startup script $xstartupFile\n";
366 open(XSTARTUP, ">$xstartupFile");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200367 print XSTARTUP $defaultXStartup;
368 close(XSTARTUP);
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400369 chmod 0755, "$xstartupFile";
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200370 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000371}
372
grayskyddff8d02015-10-19 08:24:14 -0400373# Create the user's config file if necessary.
grayskyddff8d02015-10-19 08:24:14 -0400374if (!(-e "$vncUserDir/config")) {
375 warn "Creating default config $vncUserDir/config\n";
376 open(XSTARTUP, ">$vncUserDir/config");
377 print XSTARTUP $defaultConfig;
378 close(XSTARTUP);
379 chmod 0644, "$vncUserDir/config";
380}
381
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000382# Run the X startup script.
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200383if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400384 warn "Starting applications specified in $xstartupFile\n";
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200385}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000386warn "Log file is $desktopLog\n\n";
387
388# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
389# TCP (DISPLAY=host:n)
390
391if (-e "/tmp/.X11-unix/X$displayNumber" ||
392 -e "/usr/spool/sockets/X11/$displayNumber")
393{
394 $ENV{DISPLAY}= ":$displayNumber";
395} else {
396 $ENV{DISPLAY}= "$host:$displayNumber";
397}
398$ENV{VNCDESKTOP}= $desktopName;
399
Pierre Ossmana5b37c02015-07-30 11:04:02 +0200400system($exedir."vncconfig -nowin >> " . &quotedString($desktopLog) . " 2>&1 &");
DRCf6b58402011-10-05 21:28:03 +0000401
DRC8fb11912011-03-03 10:42:14 +0000402if ($opt{'-fg'}) {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200403 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400404 system("$xstartupFile >> " . &quotedString($desktopLog) . " 2>&1");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200405 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000406 if (kill 0, `cat $pidFile`) {
407 $opt{'-kill'} = ':'.$displayNumber;
408 &Kill();
409 }
DRC8fb11912011-03-03 10:42:14 +0000410} else {
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000411 if ($opt{'-autokill'}) {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200412 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400413 system("($xstartupFile; $0 -kill :$displayNumber) >> "
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100414 . &quotedString($desktopLog) . " 2>&1 &");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200415 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000416 } else {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200417 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400418 system("$xstartupFile >> " . &quotedString($desktopLog)
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100419 . " 2>&1 &");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200420 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000421 }
DRC8fb11912011-03-03 10:42:14 +0000422}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000423
424exit;
425
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000426###############################################################################
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400427# Functions
428###############################################################################
429
430#
431# Populate the global %config hash with settings from a specified
432# vncserver configuration file if it exists
433#
434# Args: 1. file path
435# 2. optional boolean flag to enable warning when a previously
436# set configuration setting is being overridden
437#
438sub LoadConfig {
439 local ($configFile, $warnoverride) = @_;
440 local ($toggle) = undef;
441
442 if (stat($configFile)) {
443 if (open(IN, $configFile)) {
444 while (<IN>) {
445 next if /^#/;
446 if (my ($k, $v) = /^\s*(\w+)\s*=\s*(.+)$/) {
447 $k = lc($k); # must normalize key case
448 if ($warnoverride && $config{$k}) {
449 print("Warning: $configFile is overriding previously defined '$k' to be '$v'\n");
450 }
451 $config{$k} = $v;
452 } elsif ($_ =~ m/^\s*(\S+)/) {
453 # We can't reasonably warn on override of toggles (e.g. AlwaysShared)
454 # because it would get crazy to do so. We'd have to check if the
455 # current config file being loaded defined the logical opposite setting
456 # (NeverShared vs. AlwaysShared, etc etc).
457 $toggle = lc($1); # must normalize key case
458 $config{$toggle} = $k;
459 }
460 }
461 close(IN);
462 }
463 }
464}
465
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000466#
467# CheckGeometryAndDepth simply makes sure that the geometry and depth values
468# are sensible.
469#
470
471sub CheckGeometryAndDepth
472{
473 if ($geometry =~ /^(\d+)x(\d+)$/) {
474 $width = $1; $height = $2;
475
476 if (($width<1) || ($height<1)) {
477 die "$prog: geometry $geometry is invalid\n";
478 }
479
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000480 $geometry = "${width}x$height";
481 } else {
482 die "$prog: geometry $geometry is invalid\n";
483 }
484
DRCe5b4f752009-03-26 18:23:29 +0000485 if ($depth && (($depth < 8) || ($depth > 32))) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000486 die "Depth must be between 8 and 32\n";
487 }
488}
489
490
491#
492# GetDisplayNumber gets the lowest available display number. A display number
493# n is taken if something is listening on the VNC server port (5900+n) or the
494# X server port (6000+n).
495#
496
497sub GetDisplayNumber
498{
499 foreach $n (1..99) {
500 if (&CheckDisplayNumber($n)) {
501 return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
502 }
503 }
504
505 die "$prog: no free display number on $host.\n";
506}
507
508
509#
510# CheckDisplayNumber checks if the given display number is available. A
511# display number n is taken if something is listening on the VNC server port
512# (5900+n) or the X server port (6000+n).
513#
514
515sub CheckDisplayNumber
516{
517 local ($n) = @_;
518
519 socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
520 eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
521 if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
522 close(S);
523 return 0;
524 }
525 close(S);
526
527 socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
528 eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
529 if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
530 close(S);
531 return 0;
532 }
533 close(S);
534
535 if (-e "/tmp/.X$n-lock") {
536 warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
537 warn "Remove this file if there is no X server $host:$n\n";
538 return 0;
539 }
540
541 if (-e "/tmp/.X11-unix/X$n") {
542 warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
543 warn "Remove this file if there is no X server $host:$n\n";
544 return 0;
545 }
546
547 if (-e "/usr/spool/sockets/X11/$n") {
548 warn("\nWarning: $host:$n is taken because of ".
549 "/usr/spool/sockets/X11/$n\n");
550 warn "Remove this file if there is no X server $host:$n\n";
551 return 0;
552 }
553
554 return 1;
555}
556
557
558#
559# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
560# format of the current X display being used. If successful, it sets the
561# options as appropriate so that the X VNC server will use the same settings
562# (minus an allowance for window manager decorations on the geometry). Using
563# the same depth and pixel format means that the VNC server won't have to
564# translate pixels when the desktop is being viewed on this X display (for
565# TrueColor displays anyway).
566#
567
568sub GetXDisplayDefaults
569{
570 local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
571 $red, $green, $blue);
572
573 $wmDecorationWidth = 4; # a guess at typical size for window manager
574 $wmDecorationHeight = 24; # decoration size
575
576 return if (!defined($ENV{DISPLAY}));
577
578 @lines = `xdpyinfo 2>/dev/null`;
579
580 return if ($? != 0);
581
582 @matchlines = grep(/dimensions/, @lines);
583 if (@matchlines) {
584 ($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
585
586 $width -= $wmDecorationWidth;
587 $height -= $wmDecorationHeight;
588
589 $geometry = "${width}x$height";
590 }
591
592 @matchlines = grep(/default visual id/, @lines);
593 if (@matchlines) {
594 ($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
595
596 for ($i = 0; $i < @lines; $i++) {
597 if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
598 if (($lines[$i+1] !~ /TrueColor/) ||
599 ($lines[$i+2] !~ /depth/) ||
600 ($lines[$i+4] !~ /red, green, blue masks/))
601 {
602 return;
603 }
604 last;
605 }
606 }
607
608 return if ($i >= @lines);
609
610 ($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
611 ($red,$green,$blue)
612 = ($lines[$i+4]
613 =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
614
615 $red = hex($red);
616 $green = hex($green);
617 $blue = hex($blue);
618
619 if ($red > $blue) {
620 $red = int(log($red) / log(2)) - int(log($green) / log(2));
621 $green = int(log($green) / log(2)) - int(log($blue) / log(2));
622 $blue = int(log($blue) / log(2)) + 1;
623 $pixelformat = "rgb$red$green$blue";
624 } else {
625 $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
626 $green = int(log($green) / log(2)) - int(log($red) / log(2));
627 $red = int(log($red) / log(2)) + 1;
628 $pixelformat = "bgr$blue$green$red";
629 }
630 }
631}
632
633
634#
635# quotedString returns a string which yields the original string when parsed
636# by a shell.
637#
638
639sub quotedString
640{
641 local ($in) = @_;
642
643 $in =~ s/\'/\'\"\'\"\'/g;
644
645 return "'$in'";
646}
647
648
649#
650# removeSlashes turns slashes into underscores for use as a file name.
651#
652
653sub removeSlashes
654{
655 local ($in) = @_;
656
657 $in =~ s|/|_|g;
658
659 return "$in";
660}
661
662
663#
664# Usage
665#
666
667sub Usage
668{
669 die("\nusage: $prog [:<number>] [-name <desktop-name>] [-depth <depth>]\n".
670 " [-geometry <width>x<height>]\n".
671 " [-pixelformat rgbNNN|bgrNNN]\n".
DRCeed5d1f2009-03-26 19:16:19 +0000672 " [-fp <font-path>]\n".
DRC8fb11912011-03-03 10:42:14 +0000673 " [-fg]\n".
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000674 " [-autokill]\n".
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200675 " [-noxstartup]\n".
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100676 " [-xstartup <file>]\n".
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000677 " <Xvnc-options>...\n\n".
DRCb9d8e762011-02-09 08:24:58 +0000678 " $prog -kill <X-display>\n\n".
679 " $prog -list\n\n");
680}
681
682
683#
684# List
685#
686
687sub List
688{
689 opendir(dir, $vncUserDir);
690 my @filelist = readdir(dir);
691 closedir(dir);
DRC075d9fa2011-02-10 04:19:46 +0000692 print "\nTigerVNC server sessions:\n\n";
DRCb9d8e762011-02-09 08:24:58 +0000693 print "X DISPLAY #\tPROCESS ID\n";
694 foreach my $file (@filelist) {
695 if ($file =~ /$host:(\d+)$\.pid/) {
Michal Srbe6e11f92015-10-02 02:28:26 +0300696 chop($tmp_pid = `cat $vncUserDir/$file`);
697 if (kill 0, $tmp_pid) {
698 print ":".$1."\t\t".`cat $vncUserDir/$file`;
699 } else {
700 unlink ($vncUserDir . "/" . $file);
701 }
DRCb9d8e762011-02-09 08:24:58 +0000702 }
703 }
704 exit 1;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000705}
706
707
708#
709# Kill
710#
711
712sub Kill
713{
714 $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
715
716 if ($opt{'-kill'} =~ /^:\d+$/) {
717 $pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
718 } else {
719 if ($opt{'-kill'} !~ /^$host:/) {
720 die "\nCan't tell if $opt{'-kill'} is on $host\n".
721 "Use -kill :<number> instead\n\n";
722 }
723 $pidFile = "$vncUserDir/$opt{'-kill'}.pid";
724 }
725
726 if (! -r $pidFile) {
727 die "\nCan't find file $pidFile\n".
728 "You'll have to kill the Xvnc process manually\n\n";
729 }
730
731 $SIG{'HUP'} = 'IGNORE';
732 chop($pid = `cat $pidFile`);
733 warn "Killing Xvnc process ID $pid\n";
DRCb9d8e762011-02-09 08:24:58 +0000734
735 if (kill 0, $pid) {
736 system("kill $pid");
737 sleep(1);
738 if (kill 0, $pid) {
739 print "Xvnc seems to be deadlocked. Kill the process manually and then re-run\n";
740 print " ".$0." -kill ".$opt{'-kill'}."\n";
741 print "to clean up the socket files.\n";
742 exit
743 }
744
745 } else {
746 warn "Xvnc process ID $pid already killed\n";
747 $opt{'-kill'} =~ s/://;
748
749 if (-e "/tmp/.X11-unix/X$opt{'-kill'}") {
750 print "Xvnc did not appear to shut down cleanly.";
751 print " Removing /tmp/.X11-unix/X$opt{'-kill'}\n";
752 unlink "/tmp/.X11-unix/X$opt{'-kill'}";
753 }
754 if (-e "/tmp/.X$opt{'-kill'}-lock") {
755 print "Xvnc did not appear to shut down cleanly.";
756 print " Removing /tmp/.X$opt{'-kill'}-lock\n";
757 unlink "/tmp/.X$opt{'-kill'}-lock";
758 }
759 }
760
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000761 unlink $pidFile;
762 exit;
763}
764
765
766#
767# ParseOptions takes a list of possible options and a boolean indicating
768# whether the option has a value following, and sets up an associative array
769# %opt of the values of the options given on the command line. It removes all
770# the arguments it uses from @ARGV and returns them in @optArgs.
771#
772
773sub ParseOptions
774{
775 local (@optval) = @_;
776 local ($opt, @opts, %valFollows, @newargs);
777
778 while (@optval) {
779 $opt = shift(@optval);
780 push(@opts,$opt);
781 $valFollows{$opt} = shift(@optval);
782 }
783
784 @optArgs = ();
785 %opt = ();
786
787 arg: while (defined($arg = shift(@ARGV))) {
788 foreach $opt (@opts) {
789 if ($arg eq $opt) {
790 push(@optArgs, $arg);
791 if ($valFollows{$opt}) {
792 if (@ARGV == 0) {
793 &Usage();
794 }
795 $opt{$opt} = shift(@ARGV);
796 push(@optArgs, $opt{$opt});
797 } else {
798 $opt{$opt} = 1;
799 }
800 next arg;
801 }
802 }
803 push(@newargs,$arg);
804 }
805
806 @ARGV = @newargs;
807}
808
809
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000810# Routine to make sure we're operating in a sane environment.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000811sub SanityCheck
812{
813 local ($cmd);
814
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000815 # Get the program name
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000816 ($prog) = ($0 =~ m|([^/]+)$|);
817
818 #
819 # Check we have all the commands we'll need on the path.
820 #
821
822 cmd:
Pierre Ossman5fe60702015-12-29 14:27:07 +0100823 foreach $cmd ("uname","mcookie","xauth") {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000824 for (split(/:/,$ENV{PATH})) {
825 if (-x "$_/$cmd") {
826 next cmd;
827 }
828 }
829 die "$prog: couldn't find \"$cmd\" on your PATH.\n";
830 }
DRC190854c2009-03-26 18:13:00 +0000831
832 if($exedir eq "") {
833 cmd2:
834 foreach $cmd ("Xvnc","vncpasswd") {
835 for (split(/:/,$ENV{PATH})) {
836 if (-x "$_/$cmd") {
837 $vncClasses = "$_/../vnc/classes";
838 next cmd2;
839 }
840 }
841 die "$prog: couldn't find \"$cmd\" on your PATH.\n";
842 }
843 }
844 else {
845 cmd3:
846 foreach $cmd ($exedir."Xvnc",$exedir."vncpasswd") {
847 for (split(/:/,$ENV{PATH})) {
848 if (-x "$cmd") {
849 $vncClasses = $exedir."../vnc/classes";
850 next cmd3;
851 }
852 }
853 die "$prog: couldn't find \"$cmd\".\n";
854 }
855 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000856
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000857 if (!defined($ENV{HOME})) {
858 die "$prog: The HOME environment variable is not set.\n";
859 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000860
861 #
862 # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
863 # eval, and if it fails we try 'require "sys/socket.ph"'. If this fails,
864 # we just guess at the values. If you find perl moaning here, just
865 # hard-code the values of AF_INET and SOCK_STREAM. You can find these out
866 # for your platform by looking in /usr/include/sys/socket.h and related
867 # files.
868 #
869
870 chop($os = `uname`);
871 chop($osrev = `uname -r`);
872
873 eval 'use Socket';
874 if ($@) {
875 eval 'require "sys/socket.ph"';
876 if ($@) {
877 if (($os eq "SunOS") && ($osrev !~ /^4/)) {
878 $AF_INET = 2;
879 $SOCK_STREAM = 2;
880 } else {
881 $AF_INET = 2;
882 $SOCK_STREAM = 1;
883 }
884 } else {
885 $AF_INET = &AF_INET;
886 $SOCK_STREAM = &SOCK_STREAM;
887 }
888 } else {
889 $AF_INET = &AF_INET;
890 $SOCK_STREAM = &SOCK_STREAM;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000891 }
892}