Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

vncserver.in 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #!/usr/bin/env perl
  2. #
  3. # Copyright (C) 2015-2019 Pierre Ossman for Cendio AB
  4. # Copyright (C) 2009-2010 D. R. Commander. All Rights Reserved.
  5. # Copyright (C) 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
  6. # Copyright (C) 2002-2003 Constantin Kaplinsky. All Rights Reserved.
  7. # Copyright (C) 2002-2005 RealVNC Ltd.
  8. # Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
  9. #
  10. # This is free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation; either version 2 of the License, or
  13. # (at your option) any later version.
  14. #
  15. # This software is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with this software; if not, write to the Free Software
  22. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  23. # USA.
  24. #
  25. #
  26. # vncserver - wrapper script to start an X VNC server.
  27. #
  28. # First make sure we're operating in a sane environment.
  29. &SanityCheck();
  30. #
  31. # Global variables. You may want to configure some of these for
  32. # your site
  33. #
  34. $vncUserDir = rindex("$ENV{XDG_CONFIG_HOME}", "/", 0) == 0
  35. ? "$ENV{XDG_CONFIG_HOME}/tigervnc"
  36. : "$ENV{HOME}/.config/tigervnc";
  37. $vncLegacyDir = "$ENV{HOME}/.vnc";
  38. if (!stat($vncUserDir) && stat($vncLegacyDir)) {
  39. warn "~/.vnc is deprecated, please consult 'man vncsession' for paths to migrate to.";
  40. $vncUserDir = $vncLegacyDir;
  41. }
  42. $vncUserConfig = "$vncUserDir/config";
  43. $vncSystemConfigDir = "@CMAKE_INSTALL_FULL_SYSCONFDIR@/tigervnc";
  44. $vncSystemConfigDefaultsFile = "$vncSystemConfigDir/vncserver-config-defaults";
  45. $vncSystemConfigMandatoryFile = "$vncSystemConfigDir/vncserver-config-mandatory";
  46. $xauthorityFile = "$ENV{XAUTHORITY}" || "$ENV{HOME}/.Xauthority";
  47. chop($host = `uname -n`);
  48. if (-d "/etc/X11/fontpath.d") {
  49. $fontPath = "catalogue:/etc/X11/fontpath.d";
  50. }
  51. @fontpaths = ('/usr/share/X11/fonts', '/usr/share/fonts', '/usr/share/fonts/X11/');
  52. if (! -l "/usr/lib/X11") {push(@fontpaths, '/usr/lib/X11/fonts');}
  53. if (! -l "/usr/X11") {push(@fontpaths, '/usr/X11/lib/X11/fonts');}
  54. if (! -l "/usr/X11R6") {push(@fontpaths, '/usr/X11R6/lib/X11/fonts');}
  55. push(@fontpaths, '/usr/share/fonts/default');
  56. @fonttypes = ('misc',
  57. '75dpi',
  58. '100dpi',
  59. 'Speedo',
  60. 'Type1');
  61. foreach $_fpath (@fontpaths) {
  62. foreach $_ftype (@fonttypes) {
  63. if (-f "$_fpath/$_ftype/fonts.dir") {
  64. if (! -l "$_fpath/$_ftype") {
  65. $defFontPath .= "$_fpath/$_ftype,";
  66. }
  67. }
  68. }
  69. }
  70. if ($defFontPath) {
  71. if (substr($defFontPath, -1, 1) == ',') {
  72. chop $defFontPath;
  73. }
  74. }
  75. if ($fontPath eq "") {
  76. $fontPath = $defFontPath;
  77. }
  78. # Find display number.
  79. if ((@ARGV == 1) && ($ARGV[0] =~ /^:(\d+)$/)) {
  80. $displayNumber = $1;
  81. if (!&CheckDisplayNumber($displayNumber)) {
  82. die "A VNC server is already running as :$displayNumber\n";
  83. }
  84. } else {
  85. &Usage();
  86. }
  87. $vncPort = 5900 + $displayNumber;
  88. $desktopName = "$host:$displayNumber ($ENV{USER})";
  89. my %default_opts;
  90. my %config;
  91. # We set some reasonable defaults. Config file settings
  92. # override these where present.
  93. $default_opts{desktop} = $desktopName;
  94. $default_opts{auth} = $xauthorityFile;
  95. $default_opts{rfbauth} = "$vncUserDir/passwd";
  96. $default_opts{rfbport} = $vncPort;
  97. $default_opts{fp} = $fontPath if ($fontPath);
  98. $default_opts{pn} = undef;
  99. # Load user-overrideable system defaults
  100. LoadConfig($vncSystemConfigDefaultsFile);
  101. # Then the user's settings
  102. LoadConfig($vncUserConfig);
  103. # And then override anything set above if mandatory settings exist.
  104. # WARNING: "Mandatory" is used loosely here! As the man page says,
  105. # there is nothing stopping someone from EASILY subverting the
  106. # settings in $vncSystemConfigMandatoryFile by simply passing
  107. # CLI args to vncserver, which trump config files! To properly
  108. # hard force policy in a non-subvertible way would require major
  109. # development work that touches Xvnc itself.
  110. LoadConfig($vncSystemConfigMandatoryFile, 1);
  111. #
  112. # Check whether VNC authentication is enabled, and if so, check that
  113. # a VNC password has been created.
  114. #
  115. $securityTypeArgSpecified = 0;
  116. $vncAuthEnabled = 0;
  117. $passwordArgSpecified = 0;
  118. @vncAuthStrings = ("vncauth", "tlsvnc", "x509vnc");
  119. # ...first we check our configuration files' settings
  120. if ($config{'securitytypes'}) {
  121. $securityTypeArgSpecified = 1;
  122. foreach $arg2 (split(',', $config{'securitytypes'})) {
  123. if (grep {$_ eq lc($arg2)} @vncAuthStrings) {
  124. $vncAuthEnabled = 1;
  125. }
  126. }
  127. }
  128. if ($config{'password'} ||
  129. $config{'passwordfile'} ||
  130. $config{'rfbauth'}) {
  131. $passwordArgSpecified = 1;
  132. }
  133. if ((!$securityTypeArgSpecified || $vncAuthEnabled) && !$passwordArgSpecified) {
  134. ($z,$z,$mode) = stat("$vncUserDir/passwd");
  135. if (! -e "$vncUserDir/passwd") {
  136. die "VNC authentication enabled, but no password file created.\n";
  137. } elsif ($mode & 077) {
  138. die "$vncUserDir/passwd must NOT be accessible by others. Set permission to 0600.\n";
  139. }
  140. }
  141. #
  142. # Find a desktop session to run
  143. #
  144. my $sessionname;
  145. my %session;
  146. $sessionname = delete $config{'session'};
  147. if ($sessionname) {
  148. %session = LoadXSession($sessionname);
  149. if (!%session) {
  150. warn "Could not load configured desktop session $sessionname\n";
  151. $sessionname = undef;
  152. }
  153. }
  154. if (!$sessionname) {
  155. foreach $file (glob("/usr/share/xsessions/*.desktop")) {
  156. ($name) = $file =~ /^.*\/(.*)[.]desktop$/;
  157. %session = LoadXSession($name);
  158. if (%session) {
  159. $sessionname = $name;
  160. last;
  161. }
  162. }
  163. }
  164. if (!$sessionname) {
  165. die "Could not find a desktop session to run\n";
  166. }
  167. warn "Using desktop session $sessionname\n";
  168. if (!$session{'Exec'}) {
  169. die "No command specified for desktop session\n";
  170. }
  171. $ENV{GDMSESSION} = $sessionname;
  172. $ENV{DESKTOP_SESSION} = $sessionname;
  173. $ENV{XDG_SESSION_DESKTOP} = $sessionname;
  174. if ($session{'DesktopNames'}) {
  175. $ENV{XDG_CURRENT_DESKTOP} = $session{'DesktopNames'} =~ s/;/:/gr;
  176. }
  177. # Make an X server cookie and set up the Xauthority file
  178. # mcookie is a part of util-linux, usually only GNU/Linux systems have it.
  179. $cookie = `mcookie`;
  180. # Fallback for non GNU/Linux OS - use /dev/urandom on systems that have it,
  181. # otherwise use perl's random number generator, seeded with the sum
  182. # of the current time, our PID and part of the encrypted form of the password.
  183. if ($cookie eq "" && open(URANDOM, '<', '/dev/urandom')) {
  184. my $randata;
  185. if (sysread(URANDOM, $randata, 16) == 16) {
  186. $cookie = unpack 'h*', $randata;
  187. }
  188. close(URANDOM);
  189. }
  190. if ($cookie eq "") {
  191. srand(time+$$+unpack("L",`cat $vncUserDir/passwd`));
  192. for (1..16) {
  193. $cookie .= sprintf("%02x", int(rand(256)) % 256);
  194. }
  195. }
  196. open(XAUTH, "|xauth -f $xauthorityFile source -");
  197. print XAUTH "add $host:$displayNumber . $cookie\n";
  198. print XAUTH "add $host/unix:$displayNumber . $cookie\n";
  199. close(XAUTH);
  200. $ENV{XAUTHORITY} = $xauthorityFile;
  201. # Now start the X VNC Server
  202. @cmd = ("xinit");
  203. push(@cmd, $Xsession, $session{'Exec'});
  204. push(@cmd, '--');
  205. # We build up our Xvnc command with options
  206. push(@cmd, "@CMAKE_INSTALL_FULL_BINDIR@/Xvnc", ":$displayNumber");
  207. foreach my $k (sort keys %config) {
  208. push(@cmd, "-$k");
  209. push(@cmd, $config{$k}) if defined($config{$k});
  210. delete $default_opts{$k}; # file options take precedence
  211. }
  212. foreach my $k (sort keys %default_opts) {
  213. push(@cmd, "-$k");
  214. push(@cmd, $default_opts{$k}) if defined($default_opts{$k});
  215. }
  216. warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
  217. warn "Starting desktop session $sessionname\n";
  218. exec(@cmd);
  219. die "Failed to start session.\n";
  220. ###############################################################################
  221. # Functions
  222. ###############################################################################
  223. #
  224. # Populate the global %config hash with settings from a specified
  225. # vncserver configuration file if it exists
  226. #
  227. # Args: 1. file path
  228. # 2. optional boolean flag to enable warning when a previously
  229. # set configuration setting is being overridden
  230. #
  231. sub LoadConfig {
  232. local ($configFile, $warnoverride) = @_;
  233. local ($toggle) = undef;
  234. if (stat($configFile)) {
  235. if (open(IN, $configFile)) {
  236. while (<IN>) {
  237. next if /^#/;
  238. if (my ($k, $v) = /^\s*(\w+)\s*=\s*(.+)$/) {
  239. $k = lc($k); # must normalize key case
  240. if ($warnoverride && $config{$k}) {
  241. print("Warning: $configFile is overriding previously defined '$k' to be '$v'\n");
  242. }
  243. $config{$k} = $v;
  244. } elsif ($_ =~ m/^\s*(\S+)/) {
  245. # We can't reasonably warn on override of toggles (e.g. AlwaysShared)
  246. # because it would get crazy to do so. We'd have to check if the
  247. # current config file being loaded defined the logical opposite setting
  248. # (NeverShared vs. AlwaysShared, etc etc).
  249. $toggle = lc($1); # must normalize key case
  250. $config{$toggle} = undef;
  251. }
  252. }
  253. close(IN);
  254. }
  255. }
  256. }
  257. #
  258. # Load a session desktop file
  259. #
  260. sub LoadXSession {
  261. local ($name) = @_;
  262. my $file, $found_group, %session;
  263. $file = "/usr/share/xsessions/$name.desktop";
  264. if (!stat($file)) {
  265. warn "Could not find session desktop file $file";
  266. return;
  267. }
  268. if (!open(IN, $file)) {
  269. warn "Could not open session desktop file $file";
  270. return;
  271. }
  272. $found_group = 0;
  273. while (my $line = <IN>) {
  274. next if $line =~ /^#/;
  275. next if $line =~ /^\s*$/;
  276. if (!$found_group) {
  277. next if $line != "[Desktop Entry]";
  278. $found_group = 1;
  279. next;
  280. } else {
  281. last if $line =~ /^\[/;
  282. }
  283. my ($key, $value) = $line =~ /^\s*([]A-Za-z0-9_@\-\[]+)\s*=\s*(.*)$/;
  284. if (!$key) {
  285. warn "Invalid session desktop file $file";
  286. close(IN);
  287. return;
  288. }
  289. $value =~ s/\\s/ /g;
  290. $value =~ s/\\n/\n/g;
  291. $value =~ s/\\t/\t/g;
  292. $value =~ s/\\r/\r/g;
  293. $value =~ s/\\\\/\\/g;
  294. $session{$key} = $value;
  295. }
  296. close(IN);
  297. return %session;
  298. }
  299. #
  300. # CheckDisplayNumber checks if the given display number is available. A
  301. # display number n is taken if something is listening on the VNC server port
  302. # (5900+n) or the X server port (6000+n).
  303. #
  304. sub CheckDisplayNumber
  305. {
  306. my($n) = @_;
  307. use Socket;
  308. my $x11_lock_path = "/tmp/.X$n-lock";
  309. if (-e $x11_lock_path) {
  310. my($pid) = `cat "$x11_lock_path"` =~ /^\s*(\d+)\s*$/;
  311. if (defined($pid) && kill(0, $pid)) {
  312. # Lock is associated with valid PID.
  313. return 0;
  314. }
  315. }
  316. my $rfb_port = 5900 + $n;
  317. my $x11_port = 6000 + $n;
  318. for my $port ($rfb_port, $x11_port) {
  319. # Bind to port to confirm it is not in use.
  320. socket(S, PF_INET, SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
  321. setsockopt(S, SOL_SOCKET, SO_REUSEADDR, 1);
  322. if (!bind(S, sockaddr_in($port, INADDR_ANY))) {
  323. # Port is in use.
  324. close(S);
  325. return 0;
  326. }
  327. close(S);
  328. }
  329. my $x11_unix_domain = "/tmp/.X11-unix/X$n";
  330. if (-e $x11_unix_domain) {
  331. # Connect to UNIX domain socket to confirm it is not in use.
  332. socket(S, PF_UNIX, SOCK_STREAM, 0) || die "$prog: socket failed: $!\n";
  333. if (connect(S, sockaddr_un($x11_unix_domain))) {
  334. # UNIX domain socket is in use.
  335. close(S);
  336. return 0;
  337. }
  338. close(S);
  339. }
  340. return 1;
  341. }
  342. #
  343. # Usage
  344. #
  345. sub Usage
  346. {
  347. die("\nusage: $prog <display>\n\n");
  348. }
  349. # Routine to make sure we're operating in a sane environment.
  350. sub SanityCheck
  351. {
  352. local ($cmd);
  353. # Get the program name
  354. ($prog) = ($0 =~ m|([^/]+)$|);
  355. #
  356. # Check we have all the commands we'll need on the path.
  357. #
  358. cmd:
  359. foreach $cmd ("uname","xauth","xinit") {
  360. for (split(/:/,$ENV{PATH})) {
  361. if (-x "$_/$cmd") {
  362. next cmd;
  363. }
  364. }
  365. die "$prog: couldn't find \"$cmd\" on your PATH.\n";
  366. }
  367. foreach $cmd ("/etc/X11/xinit/Xsession", "/etc/X11/Xsession") {
  368. if (-x "$cmd") {
  369. $Xsession = $cmd;
  370. last;
  371. }
  372. }
  373. if (not defined $Xsession) {
  374. die "$prog: Couldn't find suitable Xsession.\n";
  375. }
  376. if (!defined($ENV{HOME})) {
  377. die "$prog: The HOME environment variable is not set.\n";
  378. }
  379. }