blob: 4c8f0bf94d27dd273173c280fd2acd6d919b126b [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Constantin Kaplinsky2c019832008-05-30 11:02:04 +00002 * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20// FIXME: Check cases when screen width/height is not a multiply of 32.
21// e.g. 800x600.
22
23#include <strings.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <errno.h>
28#include <rfb/Logger_stdio.h>
29#include <rfb/LogWriter.h>
30#include <rfb/VNCServerST.h>
31#include <rfb/Configuration.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000032#include <rfb/Timer.h>
33#include <network/TcpSocket.h>
Pierre Ossman39594b82018-05-04 15:40:22 +020034#include <network/UnixSocket.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000035
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000036#include <signal.h>
37#include <X11/X.h>
38#include <X11/Xlib.h>
39#include <X11/Xutil.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000040
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020041#include <x0vncserver/XDesktop.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000042#include <x0vncserver/Geometry.h>
43#include <x0vncserver/Image.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000044#include <x0vncserver/PollingScheduler.h>
45
Constantin Kaplinskyb9632702006-12-01 10:54:55 +000046extern char buildtime[];
47
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000048using namespace rfb;
49using namespace network;
50
51static LogWriter vlog("Main");
52
53IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
54 "cycle; actual interval may be dynamically "
55 "adjusted to satisfy MaxProcessorUsage setting", 30);
56IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
57 "CPU time to be consumed", 35);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000058StringParameter displayname("display", "The X display", "");
59IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
Pierre Ossman39594b82018-05-04 15:40:22 +020060StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
61IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000062StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
63
64//
65// Allow the main loop terminate itself gracefully on receiving a signal.
66//
67
68static bool caughtSignal = false;
69
70static void CleanupSignalHandler(int sig)
71{
72 caughtSignal = true;
73}
74
75
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000076class FileTcpFilter : public TcpFilter
77{
78
79public:
80
81 FileTcpFilter(const char *fname)
82 : TcpFilter("-"), fileName(NULL), lastModTime(0)
83 {
84 if (fname != NULL)
85 fileName = strdup((char *)fname);
86 }
87
88 virtual ~FileTcpFilter()
89 {
90 if (fileName != NULL)
91 free(fileName);
92 }
93
94 virtual bool verifyConnection(Socket* s)
95 {
96 if (!reloadRules()) {
97 vlog.error("Could not read IP filtering rules: rejecting all clients");
98 filter.clear();
99 filter.push_back(parsePattern("-"));
100 return false;
101 }
102
103 return TcpFilter::verifyConnection(s);
104 }
105
106protected:
107
108 bool reloadRules()
109 {
110 if (fileName == NULL)
111 return true;
112
113 struct stat st;
114 if (stat(fileName, &st) != 0)
115 return false;
116
117 if (st.st_mtime != lastModTime) {
118 // Actually reload only if the file was modified
119 FILE *fp = fopen(fileName, "r");
120 if (fp == NULL)
121 return false;
122
123 // Remove all the rules from the parent class
124 filter.clear();
125
126 // Parse the file contents adding rules to the parent class
127 char buf[32];
128 while (readLine(buf, 32, fp)) {
129 if (buf[0] && strchr("+-?", buf[0])) {
130 filter.push_back(parsePattern(buf));
131 }
132 }
133
134 fclose(fp);
135 lastModTime = st.st_mtime;
136 }
137 return true;
138 }
139
140protected:
141
142 char *fileName;
143 time_t lastModTime;
144
145private:
146
147 //
148 // NOTE: we silently truncate long lines in this function.
149 //
150
151 bool readLine(char *buf, int bufSize, FILE *fp)
152 {
153 if (fp == NULL || buf == NULL || bufSize == 0)
154 return false;
155
156 if (fgets(buf, bufSize, fp) == NULL)
157 return false;
158
159 char *ptr = strchr(buf, '\n');
160 if (ptr != NULL) {
161 *ptr = '\0'; // remove newline at the end
162 } else {
163 if (!feof(fp)) {
164 int c;
165 do { // skip the rest of a long line
166 c = getc(fp);
167 } while (c != '\n' && c != EOF);
168 }
169 }
170 return true;
171 }
172
173};
174
175char* programName;
176
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000177static void printVersion(FILE *fp)
178{
Peter Åstrand4eacc022009-02-27 10:12:14 +0000179 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000180 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000181}
182
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000183static void usage()
184{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000185 printVersion(stderr);
186 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
187 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000188 fprintf(stderr,"\n"
189 "Parameters can be turned on with -<param> or off with -<param>=0\n"
190 "Parameters which take a value can be specified as "
191 "-<param> <value>\n"
192 "Other valid forms are <param>=<value> -<param>=<value> "
193 "--<param>=<value>\n"
194 "Parameter names are case-insensitive. The parameters are:\n\n");
195 Configuration::listParams(79, 14);
196 exit(1);
197}
198
199int main(int argc, char** argv)
200{
201 initStdIOLoggers();
202 LogWriter::setLogParams("*:stderr:30");
203
204 programName = argv[0];
205 Display* dpy;
206
Adam Tkacc58b3d12010-04-23 13:55:10 +0000207 Configuration::enableServerParams();
208
Peter Åstrand (astrand)0a0e5822017-10-18 08:54:05 +0200209 // Disable configuration parameters which we do not support
210 Configuration::removeParam("AcceptSetDesktopSize");
211
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000212 for (int i = 1; i < argc; i++) {
213 if (Configuration::setParam(argv[i]))
214 continue;
215
216 if (argv[i][0] == '-') {
217 if (i+1 < argc) {
218 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
219 i++;
220 continue;
221 }
222 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000223 if (strcmp(argv[i], "-v") == 0 ||
224 strcmp(argv[i], "-version") == 0 ||
225 strcmp(argv[i], "--version") == 0) {
226 printVersion(stdout);
227 return 0;
228 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000229 usage();
230 }
231
232 usage();
233 }
234
235 CharArray dpyStr(displayname.getData());
236 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000237 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000238 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000239 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000240 exit(1);
241 }
242
243 signal(SIGHUP, CleanupSignalHandler);
244 signal(SIGINT, CleanupSignalHandler);
245 signal(SIGTERM, CleanupSignalHandler);
246
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200247 std::list<SocketListener*> listeners;
Tim Waugh892d10a2015-03-11 13:12:07 +0000248
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000249 try {
250 TXWindow::init(dpy,"x0vncserver");
251 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
252 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000253 if (geo.getRect().is_empty()) {
254 vlog.error("Exiting with error");
255 return 1;
256 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000257 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000258
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000259 VNCServerST server("x0vncserver", &desktop);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000260
Pierre Ossman39594b82018-05-04 15:40:22 +0200261 if (rfbunixpath.getValueStr()[0] != '\0') {
262 listeners.push_back(new network::UnixListener(rfbunixpath, rfbunixmode));
263 vlog.info("Listening on %s (mode %04o)", (const char*)rfbunixpath, (int)rfbunixmode);
264 } else {
265 createTcpListeners(&listeners, 0, (int)rfbport);
266 vlog.info("Listening on port %d", (int)rfbport);
267 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000268
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000269 const char *hostsData = hostsFile.getData();
270 FileTcpFilter fileTcpFilter(hostsData);
271 if (strlen(hostsData) != 0)
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200272 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000273 i != listeners.end();
274 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200275 (*i)->setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000276 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000277
278 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
279
280 while (!caughtSignal) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200281 int wait_ms;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000282 struct timeval tv;
Pierre Ossman16419cc2016-04-29 14:29:43 +0200283 fd_set rfds, wfds;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000284 std::list<Socket*> sockets;
285 std::list<Socket*>::iterator i;
286
287 // Process any incoming X events
288 TXWindow::handleXEvents(dpy);
289
290 FD_ZERO(&rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200291 FD_ZERO(&wfds);
292
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000293 FD_SET(ConnectionNumber(dpy), &rfds);
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200294 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000295 i != listeners.end();
296 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200297 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000298
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000299 server.getSockets(&sockets);
300 int clients_connected = 0;
301 for (i = sockets.begin(); i != sockets.end(); i++) {
302 if ((*i)->isShutdown()) {
303 server.removeSocket(*i);
304 delete (*i);
305 } else {
306 FD_SET((*i)->getFd(), &rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200307 if ((*i)->outStream().bufferUsage() > 0)
308 FD_SET((*i)->getFd(), &wfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000309 clients_connected++;
310 }
311 }
312
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000313 if (!clients_connected)
314 sched.reset();
315
Pierre Ossman278e4202016-04-29 14:28:54 +0200316 wait_ms = 0;
317
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000318 if (sched.isRunning()) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200319 wait_ms = sched.millisRemaining();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000320 if (wait_ms > 500) {
321 wait_ms = 500;
322 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000323 }
Pierre Ossman278e4202016-04-29 14:28:54 +0200324
325 soonestTimeout(&wait_ms, server.checkTimeouts());
326
327 tv.tv_sec = wait_ms / 1000;
328 tv.tv_usec = (wait_ms % 1000) * 1000;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000329
330 // Do the wait...
331 sched.sleepStarted();
Pierre Ossman16419cc2016-04-29 14:29:43 +0200332 int n = select(FD_SETSIZE, &rfds, &wfds, 0,
Pierre Ossman278e4202016-04-29 14:28:54 +0200333 wait_ms ? &tv : NULL);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000334 sched.sleepFinished();
335
336 if (n < 0) {
337 if (errno == EINTR) {
338 vlog.debug("Interrupted select() system call");
339 continue;
340 } else {
341 throw rdr::SystemException("select", errno);
342 }
343 }
344
345 // Accept new VNC connections
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200346 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000347 i != listeners.end();
348 i++) {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200349 if (FD_ISSET((*i)->getFd(), &rfds)) {
350 Socket* sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000351 if (sock) {
Pierre Ossman16419cc2016-04-29 14:29:43 +0200352 sock->outStream().setBlocking(false);
Tim Waugh892d10a2015-03-11 13:12:07 +0000353 server.addSocket(sock);
354 } else {
355 vlog.status("Client connection rejected");
356 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000357 }
358 }
359
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000360 server.checkTimeouts();
361
362 // Client list could have been changed.
363 server.getSockets(&sockets);
364
365 // Nothing more to do if there are no client connections.
366 if (sockets.empty())
367 continue;
368
369 // Process events on existing VNC connections
370 for (i = sockets.begin(); i != sockets.end(); i++) {
371 if (FD_ISSET((*i)->getFd(), &rfds))
Pierre Ossmand408ca52016-04-29 14:26:05 +0200372 server.processSocketReadEvent(*i);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200373 if (FD_ISSET((*i)->getFd(), &wfds))
374 server.processSocketWriteEvent(*i);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000375 }
376
377 if (desktop.isRunning() && sched.goodTimeToPoll()) {
378 sched.newPass();
379 desktop.poll();
380 }
381 }
382
383 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000384 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000385 return 1;
386 }
387
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000388 TXWindow::handleXEvents(dpy);
389
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000390 vlog.info("Terminated");
391 return 0;
392}