blob: 315d30c5e0206229c1c9603bf070e801dc1bfbfc [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>
34#include <tx/TXWindow.h>
35
Constantin Kaplinskya3b60c42006-06-02 04:07:49 +000036#include <vncconfig/QueryConnectDialog.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000037
38#include <signal.h>
39#include <X11/X.h>
40#include <X11/Xlib.h>
41#include <X11/Xutil.h>
42#ifdef HAVE_XTEST
43#include <X11/extensions/XTest.h>
44#endif
Pierre Ossmana7b728a2014-06-13 10:56:59 +000045#ifdef HAVE_XDAMAGE
46#include <X11/extensions/Xdamage.h>
47#endif
Alan Coopersmith9a739d42017-08-05 15:16:29 -070048#ifdef HAVE_XFIXES
49#include <X11/extensions/Xfixes.h>
50#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000051
52#include <x0vncserver/Geometry.h>
53#include <x0vncserver/Image.h>
Constantin Kaplinsky614c7b52007-12-26 18:17:09 +000054#include <x0vncserver/XPixelBuffer.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000055#include <x0vncserver/PollingScheduler.h>
56
57// XXX Lynx/OS 2.3: protos for select(), bzero()
58#ifdef Lynx
59#include <sys/proto.h>
60#endif
61
Constantin Kaplinskyb9632702006-12-01 10:54:55 +000062extern char buildtime[];
63
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000064using namespace rfb;
65using namespace network;
66
67static LogWriter vlog("Main");
68
69IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
70 "cycle; actual interval may be dynamically "
71 "adjusted to satisfy MaxProcessorUsage setting", 30);
72IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
73 "CPU time to be consumed", 35);
74BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
75BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
76 "IRIX or Solaris", true);
77StringParameter displayname("display", "The X display", "");
78IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
79IntParameter queryConnectTimeout("QueryConnectTimeout",
80 "Number of seconds to show the Accept Connection dialog before "
81 "rejecting the connection",
82 10);
83StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
84
85//
86// Allow the main loop terminate itself gracefully on receiving a signal.
87//
88
89static bool caughtSignal = false;
90
91static void CleanupSignalHandler(int sig)
92{
93 caughtSignal = true;
94}
95
96
97class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
98 public QueryResultCallback {
99public:
100 QueryConnHandler(Display* dpy, VNCServerST* vs)
101 : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
102 ~QueryConnHandler() { delete queryConnectDialog; }
103
104 // -=- VNCServerST::QueryConnectionHandler interface
105 virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
106 const char* userName,
107 char** reason) {
108 if (queryConnectSock) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000109 *reason = strDup("Another connection is currently being queried.");
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000110 return VNCServerST::REJECT;
111 }
112 if (!userName) userName = "(anonymous)";
113 queryConnectSock = sock;
114 CharArray address(sock->getPeerAddress());
115 delete queryConnectDialog;
116 queryConnectDialog = new QueryConnectDialog(display, address.buf,
117 userName, queryConnectTimeout,
118 this);
119 queryConnectDialog->map();
120 return VNCServerST::PENDING;
121 }
122
123 // -=- QueryResultCallback interface
124 virtual void queryApproved() {
125 server->approveConnection(queryConnectSock, true, 0);
126 queryConnectSock = 0;
127 }
128 virtual void queryRejected() {
129 server->approveConnection(queryConnectSock, false,
130 "Connection rejected by local user");
131 queryConnectSock = 0;
132 }
133private:
134 Display* display;
135 VNCServerST* server;
136 QueryConnectDialog* queryConnectDialog;
137 network::Socket* queryConnectSock;
138};
139
140
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100141class XDesktop : public SDesktop, public TXGlobalEventHandler
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000142{
143public:
144 XDesktop(Display* dpy_, Geometry *geometry_)
Constantin Kaplinsky303433a2008-06-04 05:57:06 +0000145 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000146 oldButtonMask(0), haveXtest(false), haveDamage(false),
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700147 haveXfixes(false), maxButtons(0), running(false)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000148 {
149#ifdef HAVE_XTEST
150 int xtestEventBase;
151 int xtestErrorBase;
152 int major, minor;
153
154 if (XTestQueryExtension(dpy, &xtestEventBase,
155 &xtestErrorBase, &major, &minor)) {
156 XTestGrabControl(dpy, True);
157 vlog.info("XTest extension present - version %d.%d",major,minor);
158 haveXtest = true;
159 } else {
160#endif
161 vlog.info("XTest extension not present");
162 vlog.info("Unable to inject events or display while server is grabbed");
163#ifdef HAVE_XTEST
164 }
165#endif
166
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000167#ifdef HAVE_XDAMAGE
168 int xdamageErrorBase;
169
170 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
171 TXWindow::setGlobalEventHandler(this);
172 haveDamage = true;
173 } else {
174#endif
175 vlog.info("DAMAGE extension not present");
176 vlog.info("Will have to poll screen for changes");
177#ifdef HAVE_XDAMAGE
178 }
179#endif
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700180
181#ifdef HAVE_XFIXES
182 int xfixesErrorBase;
183
184 if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) {
185 if (not haveDamage)
186 TXWindow::setGlobalEventHandler(this);
187 haveXfixes = true;
188 } else {
189#endif
190 vlog.info("XFIXES extension not present");
191 vlog.info("Will not be able to display cursors");
192#ifdef HAVE_XFIXES
193 }
194#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000195 }
196 virtual ~XDesktop() {
197 stop();
198 }
199
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000200 inline void poll() {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000201 if (pb and not haveDamage)
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000202 pb->poll(server);
203 }
204
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000205 // -=- SDesktop interface
206
207 virtual void start(VNCServer* vs) {
208
209 // Determine actual number of buttons of the X pointer device.
210 unsigned char btnMap[8];
211 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
212 maxButtons = (numButtons > 8) ? 8 : numButtons;
213 vlog.info("Enabling %d button%s of X pointer device",
214 maxButtons, (maxButtons != 1) ? "s" : "");
215
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000216 // Create an ImageFactory instance for producing Image objects.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000217 ImageFactory factory((bool)useShm, (bool)useOverlay);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000218
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000219 // Create pixel buffer and provide it to the server object.
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100220 pb = new XPixelBuffer(dpy, factory, geometry->getRect());
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000221 vlog.info("Allocated %s", pb->getImage()->classDesc());
222
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000223 server = (VNCServerST *)vs;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000224 server->setPixelBuffer(pb);
225
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000226#ifdef HAVE_XDAMAGE
227 if (haveDamage) {
228 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
229 XDamageReportRawRectangles);
230 }
231#endif
232
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700233#ifdef HAVE_XFIXES
234 if (haveXfixes) {
235 XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy),
236 XFixesDisplayCursorNotifyMask);
237 }
238#endif
239
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000240 running = true;
241 }
242
243 virtual void stop() {
244 running = false;
245
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000246#ifdef HAVE_XDAMAGE
247 if (haveDamage)
248 XDamageDestroy(dpy, damage);
249#endif
250
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000251 delete pb;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000252 pb = 0;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000253 }
254
255 inline bool isRunning() {
256 return running;
257 }
258
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000259 virtual void pointerEvent(const Point& pos, int buttonMask) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000260#ifdef HAVE_XTEST
261 if (!haveXtest) return;
262 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
263 geometry->offsetLeft() + pos.x,
264 geometry->offsetTop() + pos.y,
265 CurrentTime);
266 if (buttonMask != oldButtonMask) {
267 for (int i = 0; i < maxButtons; i++) {
268 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
269 if (buttonMask & (1<<i)) {
270 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
271 } else {
272 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
273 }
274 }
275 }
276 }
277 oldButtonMask = buttonMask;
278#endif
279 }
280
281 virtual void keyEvent(rdr::U32 key, bool down) {
282#ifdef HAVE_XTEST
283 if (!haveXtest) return;
284 int keycode = XKeysymToKeycode(dpy, key);
285 if (keycode)
286 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
287#endif
288 }
289
290 virtual void clientCutText(const char* str, int len) {
291 }
292
293 virtual Point getFbSize() {
294 return Point(pb->width(), pb->height());
295 }
296
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000297 // -=- TXGlobalEventHandler interface
298
299 virtual bool handleGlobalEvent(XEvent* ev) {
300#ifdef HAVE_XDAMAGE
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700301 if (ev->type == xdamageEventBase) {
302 XDamageNotifyEvent* dev;
303 Rect rect;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000304
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700305 if (!running)
306 return true;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000307
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700308 dev = (XDamageNotifyEvent*)ev;
309 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
310 server->add_changed(rect);
311
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000312 return true;
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700313 }
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000314#endif
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700315
316#ifdef HAVE_XFIXES
317 if (ev->type == xfixesEventBase + XFixesCursorNotify) {
318 XFixesCursorNotifyEvent* cev;
319 XFixesCursorImage *cim;
320
321 if (!running)
322 return true;
323
324 cev = (XFixesCursorNotifyEvent*)ev;
325
326 if (cev->subtype != XFixesDisplayCursorNotify)
327 return false;
328
329 cim = XFixesGetCursorImage(dpy);
330 if (cim == NULL)
331 return false;
332
333 // Copied from XserverDesktop::setCursor() in
334 // unix/xserver/hw/vnc/XserverDesktop.cc and adapted to
335 // handle long -> U32 conversion for 64-bit Xlib
336 rdr::U8* cursorData;
337 rdr::U8 *out;
338 const unsigned long *pixels;
339
340 cursorData = new rdr::U8[cim->width * cim->height * 4];
341
342 // Un-premultiply alpha
343 pixels = cim->pixels;
344 out = cursorData;
345 for (int y = 0; y < cim->height; y++) {
346 for (int x = 0; x < cim->width; x++) {
347 rdr::U8 alpha;
348 rdr::U32 pixel = *pixels++;
349 rdr::U8 *in = (rdr::U8 *) &pixel;
350
351 alpha = in[3];
352 if (alpha == 0)
353 alpha = 1; // Avoid division by zero
354
355 *out++ = (unsigned)*in++ * 255/alpha;
356 *out++ = (unsigned)*in++ * 255/alpha;
357 *out++ = (unsigned)*in++ * 255/alpha;
358 *out++ = *in++;
359 }
360 }
361
362 try {
363 server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot),
364 cursorData);
365 } catch (rdr::Exception& e) {
366 vlog.error("XserverDesktop::setCursor: %s",e.str());
367 }
368
369 delete [] cursorData;
370 XFree(cim);
371 return true;
372 }
373#endif
374
375 return false;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000376 }
377
378protected:
379 Display* dpy;
380 Geometry* geometry;
Constantin Kaplinsky2c019832008-05-30 11:02:04 +0000381 XPixelBuffer* pb;
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000382 VNCServerST* server;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000383 int oldButtonMask;
384 bool haveXtest;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000385 bool haveDamage;
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700386 bool haveXfixes;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000387 int maxButtons;
388 bool running;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000389#ifdef HAVE_XDAMAGE
390 Damage damage;
391 int xdamageEventBase;
392#endif
Alan Coopersmith9a739d42017-08-05 15:16:29 -0700393#ifdef HAVE_XFIXES
394 int xfixesEventBase;
395#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000396};
397
398
399class FileTcpFilter : public TcpFilter
400{
401
402public:
403
404 FileTcpFilter(const char *fname)
405 : TcpFilter("-"), fileName(NULL), lastModTime(0)
406 {
407 if (fname != NULL)
408 fileName = strdup((char *)fname);
409 }
410
411 virtual ~FileTcpFilter()
412 {
413 if (fileName != NULL)
414 free(fileName);
415 }
416
417 virtual bool verifyConnection(Socket* s)
418 {
419 if (!reloadRules()) {
420 vlog.error("Could not read IP filtering rules: rejecting all clients");
421 filter.clear();
422 filter.push_back(parsePattern("-"));
423 return false;
424 }
425
426 return TcpFilter::verifyConnection(s);
427 }
428
429protected:
430
431 bool reloadRules()
432 {
433 if (fileName == NULL)
434 return true;
435
436 struct stat st;
437 if (stat(fileName, &st) != 0)
438 return false;
439
440 if (st.st_mtime != lastModTime) {
441 // Actually reload only if the file was modified
442 FILE *fp = fopen(fileName, "r");
443 if (fp == NULL)
444 return false;
445
446 // Remove all the rules from the parent class
447 filter.clear();
448
449 // Parse the file contents adding rules to the parent class
450 char buf[32];
451 while (readLine(buf, 32, fp)) {
452 if (buf[0] && strchr("+-?", buf[0])) {
453 filter.push_back(parsePattern(buf));
454 }
455 }
456
457 fclose(fp);
458 lastModTime = st.st_mtime;
459 }
460 return true;
461 }
462
463protected:
464
465 char *fileName;
466 time_t lastModTime;
467
468private:
469
470 //
471 // NOTE: we silently truncate long lines in this function.
472 //
473
474 bool readLine(char *buf, int bufSize, FILE *fp)
475 {
476 if (fp == NULL || buf == NULL || bufSize == 0)
477 return false;
478
479 if (fgets(buf, bufSize, fp) == NULL)
480 return false;
481
482 char *ptr = strchr(buf, '\n');
483 if (ptr != NULL) {
484 *ptr = '\0'; // remove newline at the end
485 } else {
486 if (!feof(fp)) {
487 int c;
488 do { // skip the rest of a long line
489 c = getc(fp);
490 } while (c != '\n' && c != EOF);
491 }
492 }
493 return true;
494 }
495
496};
497
498char* programName;
499
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000500static void printVersion(FILE *fp)
501{
Peter Ã…strand4eacc022009-02-27 10:12:14 +0000502 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000503 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000504}
505
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000506static void usage()
507{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000508 printVersion(stderr);
509 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
510 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000511 fprintf(stderr,"\n"
512 "Parameters can be turned on with -<param> or off with -<param>=0\n"
513 "Parameters which take a value can be specified as "
514 "-<param> <value>\n"
515 "Other valid forms are <param>=<value> -<param>=<value> "
516 "--<param>=<value>\n"
517 "Parameter names are case-insensitive. The parameters are:\n\n");
518 Configuration::listParams(79, 14);
519 exit(1);
520}
521
522int main(int argc, char** argv)
523{
524 initStdIOLoggers();
525 LogWriter::setLogParams("*:stderr:30");
526
527 programName = argv[0];
528 Display* dpy;
529
Adam Tkacc58b3d12010-04-23 13:55:10 +0000530 Configuration::enableServerParams();
531
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000532 for (int i = 1; i < argc; i++) {
533 if (Configuration::setParam(argv[i]))
534 continue;
535
536 if (argv[i][0] == '-') {
537 if (i+1 < argc) {
538 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
539 i++;
540 continue;
541 }
542 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000543 if (strcmp(argv[i], "-v") == 0 ||
544 strcmp(argv[i], "-version") == 0 ||
545 strcmp(argv[i], "--version") == 0) {
546 printVersion(stdout);
547 return 0;
548 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000549 usage();
550 }
551
552 usage();
553 }
554
555 CharArray dpyStr(displayname.getData());
556 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000557 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000558 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000559 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000560 exit(1);
561 }
562
563 signal(SIGHUP, CleanupSignalHandler);
564 signal(SIGINT, CleanupSignalHandler);
565 signal(SIGTERM, CleanupSignalHandler);
566
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200567 std::list<TcpListener*> listeners;
Tim Waugh892d10a2015-03-11 13:12:07 +0000568
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000569 try {
570 TXWindow::init(dpy,"x0vncserver");
571 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
572 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000573 if (geo.getRect().is_empty()) {
574 vlog.error("Exiting with error");
575 return 1;
576 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000577 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000578
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000579 VNCServerST server("x0vncserver", &desktop);
580 QueryConnHandler qcHandler(dpy, &server);
581 server.setQueryConnectionHandler(&qcHandler);
582
Tim Waugh892d10a2015-03-11 13:12:07 +0000583 createTcpListeners(&listeners, 0, (int)rfbport);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000584 vlog.info("Listening on port %d", (int)rfbport);
585
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000586 const char *hostsData = hostsFile.getData();
587 FileTcpFilter fileTcpFilter(hostsData);
588 if (strlen(hostsData) != 0)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200589 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000590 i != listeners.end();
591 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200592 (*i)->setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000593 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000594
595 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
596
597 while (!caughtSignal) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200598 int wait_ms;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000599 struct timeval tv;
Pierre Ossman16419cc2016-04-29 14:29:43 +0200600 fd_set rfds, wfds;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000601 std::list<Socket*> sockets;
602 std::list<Socket*>::iterator i;
603
604 // Process any incoming X events
605 TXWindow::handleXEvents(dpy);
606
607 FD_ZERO(&rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200608 FD_ZERO(&wfds);
609
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000610 FD_SET(ConnectionNumber(dpy), &rfds);
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200611 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000612 i != listeners.end();
613 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200614 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000615
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000616 server.getSockets(&sockets);
617 int clients_connected = 0;
618 for (i = sockets.begin(); i != sockets.end(); i++) {
619 if ((*i)->isShutdown()) {
620 server.removeSocket(*i);
621 delete (*i);
622 } else {
623 FD_SET((*i)->getFd(), &rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200624 if ((*i)->outStream().bufferUsage() > 0)
625 FD_SET((*i)->getFd(), &wfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000626 clients_connected++;
627 }
628 }
629
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000630 if (!clients_connected)
631 sched.reset();
632
Pierre Ossman278e4202016-04-29 14:28:54 +0200633 wait_ms = 0;
634
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000635 if (sched.isRunning()) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200636 wait_ms = sched.millisRemaining();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000637 if (wait_ms > 500) {
638 wait_ms = 500;
639 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000640 }
Pierre Ossman278e4202016-04-29 14:28:54 +0200641
642 soonestTimeout(&wait_ms, server.checkTimeouts());
643
644 tv.tv_sec = wait_ms / 1000;
645 tv.tv_usec = (wait_ms % 1000) * 1000;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000646
647 // Do the wait...
648 sched.sleepStarted();
Pierre Ossman16419cc2016-04-29 14:29:43 +0200649 int n = select(FD_SETSIZE, &rfds, &wfds, 0,
Pierre Ossman278e4202016-04-29 14:28:54 +0200650 wait_ms ? &tv : NULL);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000651 sched.sleepFinished();
652
653 if (n < 0) {
654 if (errno == EINTR) {
655 vlog.debug("Interrupted select() system call");
656 continue;
657 } else {
658 throw rdr::SystemException("select", errno);
659 }
660 }
661
662 // Accept new VNC connections
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200663 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000664 i != listeners.end();
665 i++) {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200666 if (FD_ISSET((*i)->getFd(), &rfds)) {
667 Socket* sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000668 if (sock) {
Pierre Ossman16419cc2016-04-29 14:29:43 +0200669 sock->outStream().setBlocking(false);
Tim Waugh892d10a2015-03-11 13:12:07 +0000670 server.addSocket(sock);
671 } else {
672 vlog.status("Client connection rejected");
673 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000674 }
675 }
676
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000677 server.checkTimeouts();
678
679 // Client list could have been changed.
680 server.getSockets(&sockets);
681
682 // Nothing more to do if there are no client connections.
683 if (sockets.empty())
684 continue;
685
686 // Process events on existing VNC connections
687 for (i = sockets.begin(); i != sockets.end(); i++) {
688 if (FD_ISSET((*i)->getFd(), &rfds))
Pierre Ossmand408ca52016-04-29 14:26:05 +0200689 server.processSocketReadEvent(*i);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200690 if (FD_ISSET((*i)->getFd(), &wfds))
691 server.processSocketWriteEvent(*i);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000692 }
693
694 if (desktop.isRunning() && sched.goodTimeToPoll()) {
695 sched.newPass();
696 desktop.poll();
697 }
698 }
699
700 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000701 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000702 return 1;
703 }
704
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000705 TXWindow::handleXEvents(dpy);
706
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000707 vlog.info("Terminated");
708 return 0;
709}