blob: c59106ca4f6eba0202bc17d51ea7aa9bd2b4d9d5 [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
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
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900332sleep(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";
Jeff Blained6777a62016-05-23 21:43:59 -0400376 open(VNCUSERCONFIG, ">$vncUserDir/config");
377 print VNCUSERCONFIG $defaultConfig;
378 close(VNCUSERCONFIG);
grayskyddff8d02015-10-19 08:24:14 -0400379 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
DRC8fb11912011-03-03 10:42:14 +0000400if ($opt{'-fg'}) {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200401 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400402 system("$xstartupFile >> " . &quotedString($desktopLog) . " 2>&1");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200403 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000404 if (kill 0, `cat $pidFile`) {
405 $opt{'-kill'} = ':'.$displayNumber;
406 &Kill();
407 }
DRC8fb11912011-03-03 10:42:14 +0000408} else {
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000409 if ($opt{'-autokill'}) {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200410 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400411 system("($xstartupFile; $0 -kill :$displayNumber) >> "
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100412 . &quotedString($desktopLog) . " 2>&1 &");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200413 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000414 } else {
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200415 if (! $skipxstartup) {
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400416 system("$xstartupFile >> " . &quotedString($desktopLog)
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100417 . " 2>&1 &");
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200418 }
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000419 }
DRC8fb11912011-03-03 10:42:14 +0000420}
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000421
422exit;
423
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000424###############################################################################
Jeff Blaine8a9abc12016-08-06 16:22:18 -0400425# Functions
426###############################################################################
427
428#
429# Populate the global %config hash with settings from a specified
430# vncserver configuration file if it exists
431#
432# Args: 1. file path
433# 2. optional boolean flag to enable warning when a previously
434# set configuration setting is being overridden
435#
436sub LoadConfig {
437 local ($configFile, $warnoverride) = @_;
438 local ($toggle) = undef;
439
440 if (stat($configFile)) {
441 if (open(IN, $configFile)) {
442 while (<IN>) {
443 next if /^#/;
444 if (my ($k, $v) = /^\s*(\w+)\s*=\s*(.+)$/) {
445 $k = lc($k); # must normalize key case
446 if ($warnoverride && $config{$k}) {
447 print("Warning: $configFile is overriding previously defined '$k' to be '$v'\n");
448 }
449 $config{$k} = $v;
450 } elsif ($_ =~ m/^\s*(\S+)/) {
451 # We can't reasonably warn on override of toggles (e.g. AlwaysShared)
452 # because it would get crazy to do so. We'd have to check if the
453 # current config file being loaded defined the logical opposite setting
454 # (NeverShared vs. AlwaysShared, etc etc).
455 $toggle = lc($1); # must normalize key case
456 $config{$toggle} = $k;
457 }
458 }
459 close(IN);
460 }
461 }
462}
463
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000464#
465# CheckGeometryAndDepth simply makes sure that the geometry and depth values
466# are sensible.
467#
468
469sub CheckGeometryAndDepth
470{
471 if ($geometry =~ /^(\d+)x(\d+)$/) {
472 $width = $1; $height = $2;
473
474 if (($width<1) || ($height<1)) {
475 die "$prog: geometry $geometry is invalid\n";
476 }
477
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000478 $geometry = "${width}x$height";
479 } else {
480 die "$prog: geometry $geometry is invalid\n";
481 }
482
DRCe5b4f752009-03-26 18:23:29 +0000483 if ($depth && (($depth < 8) || ($depth > 32))) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000484 die "Depth must be between 8 and 32\n";
485 }
486}
487
488
489#
490# GetDisplayNumber gets the lowest available display number. A display number
491# n is taken if something is listening on the VNC server port (5900+n) or the
492# X server port (6000+n).
493#
494
495sub GetDisplayNumber
496{
497 foreach $n (1..99) {
498 if (&CheckDisplayNumber($n)) {
499 return $n+0; # Bruce Mah's workaround for bug in perl 5.005_02
500 }
501 }
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900502
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000503 die "$prog: no free display number on $host.\n";
504}
505
506
507#
508# CheckDisplayNumber checks if the given display number is available. A
509# display number n is taken if something is listening on the VNC server port
510# (5900+n) or the X server port (6000+n).
511#
512
513sub CheckDisplayNumber
514{
515 local ($n) = @_;
516
517 socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
518 eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
519 if (!bind(S, pack('S n x12', $AF_INET, 6000 + $n))) {
520 close(S);
521 return 0;
522 }
523 close(S);
524
525 socket(S, $AF_INET, $SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
526 eval 'setsockopt(S, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1))';
527 if (!bind(S, pack('S n x12', $AF_INET, 5900 + $n))) {
528 close(S);
529 return 0;
530 }
531 close(S);
532
533 if (-e "/tmp/.X$n-lock") {
534 warn "\nWarning: $host:$n is taken because of /tmp/.X$n-lock\n";
535 warn "Remove this file if there is no X server $host:$n\n";
536 return 0;
537 }
538
539 if (-e "/tmp/.X11-unix/X$n") {
540 warn "\nWarning: $host:$n is taken because of /tmp/.X11-unix/X$n\n";
541 warn "Remove this file if there is no X server $host:$n\n";
542 return 0;
543 }
544
545 if (-e "/usr/spool/sockets/X11/$n") {
546 warn("\nWarning: $host:$n is taken because of ".
547 "/usr/spool/sockets/X11/$n\n");
548 warn "Remove this file if there is no X server $host:$n\n";
549 return 0;
550 }
551
552 return 1;
553}
554
555
556#
557# GetXDisplayDefaults uses xdpyinfo to find out the geometry, depth and pixel
558# format of the current X display being used. If successful, it sets the
559# options as appropriate so that the X VNC server will use the same settings
560# (minus an allowance for window manager decorations on the geometry). Using
561# the same depth and pixel format means that the VNC server won't have to
562# translate pixels when the desktop is being viewed on this X display (for
563# TrueColor displays anyway).
564#
565
566sub GetXDisplayDefaults
567{
568 local (@lines, @matchlines, $width, $height, $defaultVisualId, $i,
569 $red, $green, $blue);
570
571 $wmDecorationWidth = 4; # a guess at typical size for window manager
572 $wmDecorationHeight = 24; # decoration size
573
574 return if (!defined($ENV{DISPLAY}));
575
576 @lines = `xdpyinfo 2>/dev/null`;
577
578 return if ($? != 0);
579
580 @matchlines = grep(/dimensions/, @lines);
581 if (@matchlines) {
582 ($width, $height) = ($matchlines[0] =~ /(\d+)x(\d+) pixels/);
583
584 $width -= $wmDecorationWidth;
585 $height -= $wmDecorationHeight;
586
587 $geometry = "${width}x$height";
588 }
589
590 @matchlines = grep(/default visual id/, @lines);
591 if (@matchlines) {
592 ($defaultVisualId) = ($matchlines[0] =~ /id:\s+(\S+)/);
593
594 for ($i = 0; $i < @lines; $i++) {
595 if ($lines[$i] =~ /^\s*visual id:\s+$defaultVisualId$/) {
596 if (($lines[$i+1] !~ /TrueColor/) ||
597 ($lines[$i+2] !~ /depth/) ||
598 ($lines[$i+4] !~ /red, green, blue masks/))
599 {
600 return;
601 }
602 last;
603 }
604 }
605
606 return if ($i >= @lines);
607
608 ($depth) = ($lines[$i+2] =~ /depth:\s+(\d+)/);
609 ($red,$green,$blue)
610 = ($lines[$i+4]
611 =~ /masks:\s+0x([0-9a-f]+), 0x([0-9a-f]+), 0x([0-9a-f]+)/);
612
613 $red = hex($red);
614 $green = hex($green);
615 $blue = hex($blue);
616
617 if ($red > $blue) {
618 $red = int(log($red) / log(2)) - int(log($green) / log(2));
619 $green = int(log($green) / log(2)) - int(log($blue) / log(2));
620 $blue = int(log($blue) / log(2)) + 1;
621 $pixelformat = "rgb$red$green$blue";
622 } else {
623 $blue = int(log($blue) / log(2)) - int(log($green) / log(2));
624 $green = int(log($green) / log(2)) - int(log($red) / log(2));
625 $red = int(log($red) / log(2)) + 1;
626 $pixelformat = "bgr$blue$green$red";
627 }
628 }
629}
630
631
632#
633# quotedString returns a string which yields the original string when parsed
634# by a shell.
635#
636
637sub quotedString
638{
639 local ($in) = @_;
640
641 $in =~ s/\'/\'\"\'\"\'/g;
642
643 return "'$in'";
644}
645
646
647#
648# removeSlashes turns slashes into underscores for use as a file name.
649#
650
651sub removeSlashes
652{
653 local ($in) = @_;
654
655 $in =~ s|/|_|g;
656
657 return "$in";
658}
659
660
661#
662# Usage
663#
664
665sub Usage
666{
667 die("\nusage: $prog [:<number>] [-name <desktop-name>] [-depth <depth>]\n".
668 " [-geometry <width>x<height>]\n".
669 " [-pixelformat rgbNNN|bgrNNN]\n".
DRCeed5d1f2009-03-26 19:16:19 +0000670 " [-fp <font-path>]\n".
DRC8fb11912011-03-03 10:42:14 +0000671 " [-fg]\n".
Adam Tkac38ba8cf2011-04-27 11:28:09 +0000672 " [-autokill]\n".
Llorenç Garcia Martinez861cb062015-10-23 13:37:42 +0200673 " [-noxstartup]\n".
Llorenç Garcia Martineze76c2fb2015-10-30 11:07:40 +0100674 " [-xstartup <file>]\n".
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000675 " <Xvnc-options>...\n\n".
DRCb9d8e762011-02-09 08:24:58 +0000676 " $prog -kill <X-display>\n\n".
677 " $prog -list\n\n");
678}
679
680
681#
682# List
683#
684
685sub List
686{
687 opendir(dir, $vncUserDir);
688 my @filelist = readdir(dir);
689 closedir(dir);
DRC075d9fa2011-02-10 04:19:46 +0000690 print "\nTigerVNC server sessions:\n\n";
DRCb9d8e762011-02-09 08:24:58 +0000691 print "X DISPLAY #\tPROCESS ID\n";
692 foreach my $file (@filelist) {
693 if ($file =~ /$host:(\d+)$\.pid/) {
Michal Srbe6e11f92015-10-02 02:28:26 +0300694 chop($tmp_pid = `cat $vncUserDir/$file`);
695 if (kill 0, $tmp_pid) {
696 print ":".$1."\t\t".`cat $vncUserDir/$file`;
697 } else {
698 unlink ($vncUserDir . "/" . $file);
699 }
DRCb9d8e762011-02-09 08:24:58 +0000700 }
701 }
702 exit 1;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000703}
704
705
706#
707# Kill
708#
709
710sub Kill
711{
712 $opt{'-kill'} =~ s/(:\d+)\.\d+$/$1/; # e.g. turn :1.0 into :1
713
714 if ($opt{'-kill'} =~ /^:\d+$/) {
715 $pidFile = "$vncUserDir/$host$opt{'-kill'}.pid";
716 } else {
717 if ($opt{'-kill'} !~ /^$host:/) {
718 die "\nCan't tell if $opt{'-kill'} is on $host\n".
719 "Use -kill :<number> instead\n\n";
720 }
721 $pidFile = "$vncUserDir/$opt{'-kill'}.pid";
722 }
723
724 if (! -r $pidFile) {
725 die "\nCan't find file $pidFile\n".
726 "You'll have to kill the Xvnc process manually\n\n";
727 }
728
729 $SIG{'HUP'} = 'IGNORE';
730 chop($pid = `cat $pidFile`);
731 warn "Killing Xvnc process ID $pid\n";
DRCb9d8e762011-02-09 08:24:58 +0000732
733 if (kill 0, $pid) {
734 system("kill $pid");
735 sleep(1);
736 if (kill 0, $pid) {
737 print "Xvnc seems to be deadlocked. Kill the process manually and then re-run\n";
738 print " ".$0." -kill ".$opt{'-kill'}."\n";
739 print "to clean up the socket files.\n";
740 exit
741 }
742
743 } else {
744 warn "Xvnc process ID $pid already killed\n";
745 $opt{'-kill'} =~ s/://;
Koichiro IWAO977fbde2016-10-03 18:07:57 +0900746
DRCb9d8e762011-02-09 08:24:58 +0000747 if (-e "/tmp/.X11-unix/X$opt{'-kill'}") {
748 print "Xvnc did not appear to shut down cleanly.";
749 print " Removing /tmp/.X11-unix/X$opt{'-kill'}\n";
750 unlink "/tmp/.X11-unix/X$opt{'-kill'}";
751 }
752 if (-e "/tmp/.X$opt{'-kill'}-lock") {
753 print "Xvnc did not appear to shut down cleanly.";
754 print " Removing /tmp/.X$opt{'-kill'}-lock\n";
755 unlink "/tmp/.X$opt{'-kill'}-lock";
756 }
757 }
758
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000759 unlink $pidFile;
760 exit;
761}
762
763
764#
765# ParseOptions takes a list of possible options and a boolean indicating
766# whether the option has a value following, and sets up an associative array
767# %opt of the values of the options given on the command line. It removes all
768# the arguments it uses from @ARGV and returns them in @optArgs.
769#
770
771sub ParseOptions
772{
773 local (@optval) = @_;
774 local ($opt, @opts, %valFollows, @newargs);
775
776 while (@optval) {
777 $opt = shift(@optval);
778 push(@opts,$opt);
779 $valFollows{$opt} = shift(@optval);
780 }
781
782 @optArgs = ();
783 %opt = ();
784
785 arg: while (defined($arg = shift(@ARGV))) {
786 foreach $opt (@opts) {
787 if ($arg eq $opt) {
788 push(@optArgs, $arg);
789 if ($valFollows{$opt}) {
790 if (@ARGV == 0) {
791 &Usage();
792 }
793 $opt{$opt} = shift(@ARGV);
794 push(@optArgs, $opt{$opt});
795 } else {
796 $opt{$opt} = 1;
797 }
798 next arg;
799 }
800 }
801 push(@newargs,$arg);
802 }
803
804 @ARGV = @newargs;
805}
806
807
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000808# Routine to make sure we're operating in a sane environment.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000809sub SanityCheck
810{
811 local ($cmd);
812
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000813 # Get the program name
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000814 ($prog) = ($0 =~ m|([^/]+)$|);
815
816 #
817 # Check we have all the commands we'll need on the path.
818 #
819
820 cmd:
Pierre Ossman5fe60702015-12-29 14:27:07 +0100821 foreach $cmd ("uname","mcookie","xauth") {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000822 for (split(/:/,$ENV{PATH})) {
823 if (-x "$_/$cmd") {
824 next cmd;
825 }
826 }
827 die "$prog: couldn't find \"$cmd\" on your PATH.\n";
828 }
DRC190854c2009-03-26 18:13:00 +0000829
830 if($exedir eq "") {
831 cmd2:
832 foreach $cmd ("Xvnc","vncpasswd") {
833 for (split(/:/,$ENV{PATH})) {
834 if (-x "$_/$cmd") {
835 $vncClasses = "$_/../vnc/classes";
836 next cmd2;
837 }
838 }
839 die "$prog: couldn't find \"$cmd\" on your PATH.\n";
840 }
841 }
842 else {
843 cmd3:
844 foreach $cmd ($exedir."Xvnc",$exedir."vncpasswd") {
845 for (split(/:/,$ENV{PATH})) {
846 if (-x "$cmd") {
847 $vncClasses = $exedir."../vnc/classes";
848 next cmd3;
849 }
850 }
851 die "$prog: couldn't find \"$cmd\".\n";
852 }
853 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000854
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000855 if (!defined($ENV{HOME})) {
856 die "$prog: The HOME environment variable is not set.\n";
857 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000858
859 #
860 # Find socket constants. 'use Socket' is a perl5-ism, so we wrap it in an
861 # eval, and if it fails we try 'require "sys/socket.ph"'. If this fails,
862 # we just guess at the values. If you find perl moaning here, just
863 # hard-code the values of AF_INET and SOCK_STREAM. You can find these out
864 # for your platform by looking in /usr/include/sys/socket.h and related
865 # files.
866 #
867
868 chop($os = `uname`);
869 chop($osrev = `uname -r`);
870
871 eval 'use Socket';
872 if ($@) {
873 eval 'require "sys/socket.ph"';
874 if ($@) {
875 if (($os eq "SunOS") && ($osrev !~ /^4/)) {
876 $AF_INET = 2;
877 $SOCK_STREAM = 2;
878 } else {
879 $AF_INET = 2;
880 $SOCK_STREAM = 1;
881 }
882 } else {
883 $AF_INET = &AF_INET;
884 $SOCK_STREAM = &SOCK_STREAM;
885 }
886 } else {
887 $AF_INET = &AF_INET;
888 $SOCK_STREAM = &SOCK_STREAM;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000889 }
890}