blob: 9e5da4f9d1bfe963c83f300ab93ae39278558922 [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>
Peter Korsgaard5b7ff372017-07-13 00:36:01 +020042#include <X11/XKBlib.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000043#ifdef HAVE_XTEST
44#include <X11/extensions/XTest.h>
45#endif
Pierre Ossmana7b728a2014-06-13 10:56:59 +000046#ifdef HAVE_XDAMAGE
47#include <X11/extensions/Xdamage.h>
48#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000049
50#include <x0vncserver/Geometry.h>
51#include <x0vncserver/Image.h>
Constantin Kaplinsky614c7b52007-12-26 18:17:09 +000052#include <x0vncserver/XPixelBuffer.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000053#include <x0vncserver/PollingScheduler.h>
54
55// XXX Lynx/OS 2.3: protos for select(), bzero()
56#ifdef Lynx
57#include <sys/proto.h>
58#endif
59
Constantin Kaplinskyb9632702006-12-01 10:54:55 +000060extern char buildtime[];
61
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000062using namespace rfb;
63using namespace network;
64
Peter Korsgaard5b7ff372017-07-13 00:36:01 +020065// number of XKb indicator leds to handle
66static const int N_LEDS = 3;
67
68// order is important as it must match RFB extension
69static const char * ledNames[N_LEDS] = {
70 "Scroll Lock", "Num Lock", "Caps Lock"
71};
72
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000073static LogWriter vlog("Main");
74
75IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
76 "cycle; actual interval may be dynamically "
77 "adjusted to satisfy MaxProcessorUsage setting", 30);
78IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
79 "CPU time to be consumed", 35);
80BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
81BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
82 "IRIX or Solaris", true);
83StringParameter displayname("display", "The X display", "");
84IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
85IntParameter queryConnectTimeout("QueryConnectTimeout",
86 "Number of seconds to show the Accept Connection dialog before "
87 "rejecting the connection",
88 10);
89StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
90
91//
92// Allow the main loop terminate itself gracefully on receiving a signal.
93//
94
95static bool caughtSignal = false;
96
97static void CleanupSignalHandler(int sig)
98{
99 caughtSignal = true;
100}
101
102
103class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
104 public QueryResultCallback {
105public:
106 QueryConnHandler(Display* dpy, VNCServerST* vs)
107 : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
108 ~QueryConnHandler() { delete queryConnectDialog; }
109
110 // -=- VNCServerST::QueryConnectionHandler interface
111 virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
112 const char* userName,
113 char** reason) {
114 if (queryConnectSock) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000115 *reason = strDup("Another connection is currently being queried.");
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000116 return VNCServerST::REJECT;
117 }
118 if (!userName) userName = "(anonymous)";
119 queryConnectSock = sock;
120 CharArray address(sock->getPeerAddress());
121 delete queryConnectDialog;
122 queryConnectDialog = new QueryConnectDialog(display, address.buf,
123 userName, queryConnectTimeout,
124 this);
125 queryConnectDialog->map();
126 return VNCServerST::PENDING;
127 }
128
129 // -=- QueryResultCallback interface
130 virtual void queryApproved() {
131 server->approveConnection(queryConnectSock, true, 0);
132 queryConnectSock = 0;
133 }
134 virtual void queryRejected() {
135 server->approveConnection(queryConnectSock, false,
136 "Connection rejected by local user");
137 queryConnectSock = 0;
138 }
139private:
140 Display* display;
141 VNCServerST* server;
142 QueryConnectDialog* queryConnectDialog;
143 network::Socket* queryConnectSock;
144};
145
146
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100147class XDesktop : public SDesktop, public TXGlobalEventHandler
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000148{
149public:
150 XDesktop(Display* dpy_, Geometry *geometry_)
Constantin Kaplinsky303433a2008-06-04 05:57:06 +0000151 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000152 oldButtonMask(0), haveXtest(false), haveDamage(false),
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200153 maxButtons(0), running(false), ledMasks(), ledState(0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000154 {
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200155 int major, minor;
156
157 int xkbOpcode, xkbErrorBase;
158
159 major = XkbMajorVersion;
160 minor = XkbMinorVersion;
161 if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
162 &xkbErrorBase, &major, &minor)) {
163 vlog.error("XKEYBOARD extension not present");
164 throw Exception();
165 }
166
167 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
168 XkbIndicatorStateNotifyMask);
169
170 // figure out bit masks for the indicators we are interested in
171 for (int i = 0; i < N_LEDS; i++) {
172 Atom a;
173 int shift;
174 Bool on;
175
176 a = XInternAtom(dpy, ledNames[i], True);
177 if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
178 continue;
179
180 ledMasks[i] = 1u << shift;
181 vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
182 if (on)
183 ledState |= 1u << i;
184 }
185
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000186#ifdef HAVE_XTEST
187 int xtestEventBase;
188 int xtestErrorBase;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000189
190 if (XTestQueryExtension(dpy, &xtestEventBase,
191 &xtestErrorBase, &major, &minor)) {
192 XTestGrabControl(dpy, True);
193 vlog.info("XTest extension present - version %d.%d",major,minor);
194 haveXtest = true;
195 } else {
196#endif
197 vlog.info("XTest extension not present");
198 vlog.info("Unable to inject events or display while server is grabbed");
199#ifdef HAVE_XTEST
200 }
201#endif
202
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000203#ifdef HAVE_XDAMAGE
204 int xdamageErrorBase;
205
206 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000207 haveDamage = true;
208 } else {
209#endif
210 vlog.info("DAMAGE extension not present");
211 vlog.info("Will have to poll screen for changes");
212#ifdef HAVE_XDAMAGE
213 }
214#endif
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200215
216 TXWindow::setGlobalEventHandler(this);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000217 }
218 virtual ~XDesktop() {
219 stop();
220 }
221
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000222 inline void poll() {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000223 if (pb and not haveDamage)
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000224 pb->poll(server);
225 }
226
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000227 // -=- SDesktop interface
228
229 virtual void start(VNCServer* vs) {
230
231 // Determine actual number of buttons of the X pointer device.
232 unsigned char btnMap[8];
233 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
234 maxButtons = (numButtons > 8) ? 8 : numButtons;
235 vlog.info("Enabling %d button%s of X pointer device",
236 maxButtons, (maxButtons != 1) ? "s" : "");
237
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000238 // Create an ImageFactory instance for producing Image objects.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000239 ImageFactory factory((bool)useShm, (bool)useOverlay);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000240
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000241 // Create pixel buffer and provide it to the server object.
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100242 pb = new XPixelBuffer(dpy, factory, geometry->getRect());
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000243 vlog.info("Allocated %s", pb->getImage()->classDesc());
244
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000245 server = (VNCServerST *)vs;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000246 server->setPixelBuffer(pb);
247
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000248#ifdef HAVE_XDAMAGE
249 if (haveDamage) {
250 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
251 XDamageReportRawRectangles);
252 }
253#endif
254
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200255 server->setLEDState(ledState);
256
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000257 running = true;
258 }
259
260 virtual void stop() {
261 running = false;
262
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000263#ifdef HAVE_XDAMAGE
264 if (haveDamage)
265 XDamageDestroy(dpy, damage);
266#endif
267
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000268 delete pb;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000269 pb = 0;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000270 }
271
272 inline bool isRunning() {
273 return running;
274 }
275
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000276 virtual void pointerEvent(const Point& pos, int buttonMask) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000277#ifdef HAVE_XTEST
278 if (!haveXtest) return;
279 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
280 geometry->offsetLeft() + pos.x,
281 geometry->offsetTop() + pos.y,
282 CurrentTime);
283 if (buttonMask != oldButtonMask) {
284 for (int i = 0; i < maxButtons; i++) {
285 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
286 if (buttonMask & (1<<i)) {
287 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
288 } else {
289 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
290 }
291 }
292 }
293 }
294 oldButtonMask = buttonMask;
295#endif
296 }
297
298 virtual void keyEvent(rdr::U32 key, bool down) {
299#ifdef HAVE_XTEST
300 if (!haveXtest) return;
301 int keycode = XKeysymToKeycode(dpy, key);
302 if (keycode)
303 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
304#endif
305 }
306
307 virtual void clientCutText(const char* str, int len) {
308 }
309
310 virtual Point getFbSize() {
311 return Point(pb->width(), pb->height());
312 }
313
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000314 // -=- TXGlobalEventHandler interface
315
316 virtual bool handleGlobalEvent(XEvent* ev) {
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200317 if (ev->type == xkbEventBase + XkbEventCode) {
318 XkbEvent *kb = (XkbEvent *)ev;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000319
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200320 if (kb->any.xkb_type != XkbIndicatorStateNotify)
321 return false;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000322
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200323 vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
324
325 ledState = 0;
326 for (int i = 0; i < N_LEDS; i++) {
327 if (kb->indicators.state & ledMasks[i])
328 ledState |= 1u << i;
329 }
330
331 if (running)
332 server->setLEDState(ledState);
333
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000334 return true;
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200335#ifdef HAVE_XDAMAGE
336 } else if (ev->type == xdamageEventBase) {
337 XDamageNotifyEvent* dev;
338 Rect rect;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000339
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200340 if (!running)
341 return true;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000342
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200343 dev = (XDamageNotifyEvent*)ev;
344 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
345 server->add_changed(rect);
346
347 return true;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000348#endif
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200349 }
350
351 return false;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000352 }
353
354protected:
355 Display* dpy;
356 Geometry* geometry;
Constantin Kaplinsky2c019832008-05-30 11:02:04 +0000357 XPixelBuffer* pb;
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000358 VNCServerST* server;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000359 int oldButtonMask;
360 bool haveXtest;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000361 bool haveDamage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000362 int maxButtons;
363 bool running;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000364#ifdef HAVE_XDAMAGE
365 Damage damage;
366 int xdamageEventBase;
367#endif
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200368 int xkbEventBase;
369 int ledMasks[N_LEDS];
370 unsigned ledState;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000371};
372
373
374class FileTcpFilter : public TcpFilter
375{
376
377public:
378
379 FileTcpFilter(const char *fname)
380 : TcpFilter("-"), fileName(NULL), lastModTime(0)
381 {
382 if (fname != NULL)
383 fileName = strdup((char *)fname);
384 }
385
386 virtual ~FileTcpFilter()
387 {
388 if (fileName != NULL)
389 free(fileName);
390 }
391
392 virtual bool verifyConnection(Socket* s)
393 {
394 if (!reloadRules()) {
395 vlog.error("Could not read IP filtering rules: rejecting all clients");
396 filter.clear();
397 filter.push_back(parsePattern("-"));
398 return false;
399 }
400
401 return TcpFilter::verifyConnection(s);
402 }
403
404protected:
405
406 bool reloadRules()
407 {
408 if (fileName == NULL)
409 return true;
410
411 struct stat st;
412 if (stat(fileName, &st) != 0)
413 return false;
414
415 if (st.st_mtime != lastModTime) {
416 // Actually reload only if the file was modified
417 FILE *fp = fopen(fileName, "r");
418 if (fp == NULL)
419 return false;
420
421 // Remove all the rules from the parent class
422 filter.clear();
423
424 // Parse the file contents adding rules to the parent class
425 char buf[32];
426 while (readLine(buf, 32, fp)) {
427 if (buf[0] && strchr("+-?", buf[0])) {
428 filter.push_back(parsePattern(buf));
429 }
430 }
431
432 fclose(fp);
433 lastModTime = st.st_mtime;
434 }
435 return true;
436 }
437
438protected:
439
440 char *fileName;
441 time_t lastModTime;
442
443private:
444
445 //
446 // NOTE: we silently truncate long lines in this function.
447 //
448
449 bool readLine(char *buf, int bufSize, FILE *fp)
450 {
451 if (fp == NULL || buf == NULL || bufSize == 0)
452 return false;
453
454 if (fgets(buf, bufSize, fp) == NULL)
455 return false;
456
457 char *ptr = strchr(buf, '\n');
458 if (ptr != NULL) {
459 *ptr = '\0'; // remove newline at the end
460 } else {
461 if (!feof(fp)) {
462 int c;
463 do { // skip the rest of a long line
464 c = getc(fp);
465 } while (c != '\n' && c != EOF);
466 }
467 }
468 return true;
469 }
470
471};
472
473char* programName;
474
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000475static void printVersion(FILE *fp)
476{
Peter Ã…strand4eacc022009-02-27 10:12:14 +0000477 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000478 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000479}
480
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000481static void usage()
482{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000483 printVersion(stderr);
484 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
485 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000486 fprintf(stderr,"\n"
487 "Parameters can be turned on with -<param> or off with -<param>=0\n"
488 "Parameters which take a value can be specified as "
489 "-<param> <value>\n"
490 "Other valid forms are <param>=<value> -<param>=<value> "
491 "--<param>=<value>\n"
492 "Parameter names are case-insensitive. The parameters are:\n\n");
493 Configuration::listParams(79, 14);
494 exit(1);
495}
496
497int main(int argc, char** argv)
498{
499 initStdIOLoggers();
500 LogWriter::setLogParams("*:stderr:30");
501
502 programName = argv[0];
503 Display* dpy;
504
Adam Tkacc58b3d12010-04-23 13:55:10 +0000505 Configuration::enableServerParams();
506
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000507 for (int i = 1; i < argc; i++) {
508 if (Configuration::setParam(argv[i]))
509 continue;
510
511 if (argv[i][0] == '-') {
512 if (i+1 < argc) {
513 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
514 i++;
515 continue;
516 }
517 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000518 if (strcmp(argv[i], "-v") == 0 ||
519 strcmp(argv[i], "-version") == 0 ||
520 strcmp(argv[i], "--version") == 0) {
521 printVersion(stdout);
522 return 0;
523 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000524 usage();
525 }
526
527 usage();
528 }
529
530 CharArray dpyStr(displayname.getData());
531 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000532 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000533 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000534 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000535 exit(1);
536 }
537
538 signal(SIGHUP, CleanupSignalHandler);
539 signal(SIGINT, CleanupSignalHandler);
540 signal(SIGTERM, CleanupSignalHandler);
541
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200542 std::list<TcpListener*> listeners;
Tim Waugh892d10a2015-03-11 13:12:07 +0000543
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000544 try {
545 TXWindow::init(dpy,"x0vncserver");
546 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
547 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000548 if (geo.getRect().is_empty()) {
549 vlog.error("Exiting with error");
550 return 1;
551 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000552 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000553
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000554 VNCServerST server("x0vncserver", &desktop);
555 QueryConnHandler qcHandler(dpy, &server);
556 server.setQueryConnectionHandler(&qcHandler);
557
Tim Waugh892d10a2015-03-11 13:12:07 +0000558 createTcpListeners(&listeners, 0, (int)rfbport);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000559 vlog.info("Listening on port %d", (int)rfbport);
560
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000561 const char *hostsData = hostsFile.getData();
562 FileTcpFilter fileTcpFilter(hostsData);
563 if (strlen(hostsData) != 0)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200564 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000565 i != listeners.end();
566 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200567 (*i)->setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000568 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000569
570 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
571
572 while (!caughtSignal) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200573 int wait_ms;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000574 struct timeval tv;
Pierre Ossman16419cc2016-04-29 14:29:43 +0200575 fd_set rfds, wfds;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000576 std::list<Socket*> sockets;
577 std::list<Socket*>::iterator i;
578
579 // Process any incoming X events
580 TXWindow::handleXEvents(dpy);
581
582 FD_ZERO(&rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200583 FD_ZERO(&wfds);
584
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000585 FD_SET(ConnectionNumber(dpy), &rfds);
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200586 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000587 i != listeners.end();
588 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200589 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000590
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000591 server.getSockets(&sockets);
592 int clients_connected = 0;
593 for (i = sockets.begin(); i != sockets.end(); i++) {
594 if ((*i)->isShutdown()) {
595 server.removeSocket(*i);
596 delete (*i);
597 } else {
598 FD_SET((*i)->getFd(), &rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200599 if ((*i)->outStream().bufferUsage() > 0)
600 FD_SET((*i)->getFd(), &wfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000601 clients_connected++;
602 }
603 }
604
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000605 if (!clients_connected)
606 sched.reset();
607
Pierre Ossman278e4202016-04-29 14:28:54 +0200608 wait_ms = 0;
609
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000610 if (sched.isRunning()) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200611 wait_ms = sched.millisRemaining();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000612 if (wait_ms > 500) {
613 wait_ms = 500;
614 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000615 }
Pierre Ossman278e4202016-04-29 14:28:54 +0200616
617 soonestTimeout(&wait_ms, server.checkTimeouts());
618
619 tv.tv_sec = wait_ms / 1000;
620 tv.tv_usec = (wait_ms % 1000) * 1000;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000621
622 // Do the wait...
623 sched.sleepStarted();
Pierre Ossman16419cc2016-04-29 14:29:43 +0200624 int n = select(FD_SETSIZE, &rfds, &wfds, 0,
Pierre Ossman278e4202016-04-29 14:28:54 +0200625 wait_ms ? &tv : NULL);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000626 sched.sleepFinished();
627
628 if (n < 0) {
629 if (errno == EINTR) {
630 vlog.debug("Interrupted select() system call");
631 continue;
632 } else {
633 throw rdr::SystemException("select", errno);
634 }
635 }
636
637 // Accept new VNC connections
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200638 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000639 i != listeners.end();
640 i++) {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200641 if (FD_ISSET((*i)->getFd(), &rfds)) {
642 Socket* sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000643 if (sock) {
Pierre Ossman16419cc2016-04-29 14:29:43 +0200644 sock->outStream().setBlocking(false);
Tim Waugh892d10a2015-03-11 13:12:07 +0000645 server.addSocket(sock);
646 } else {
647 vlog.status("Client connection rejected");
648 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000649 }
650 }
651
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000652 server.checkTimeouts();
653
654 // Client list could have been changed.
655 server.getSockets(&sockets);
656
657 // Nothing more to do if there are no client connections.
658 if (sockets.empty())
659 continue;
660
661 // Process events on existing VNC connections
662 for (i = sockets.begin(); i != sockets.end(); i++) {
663 if (FD_ISSET((*i)->getFd(), &rfds))
Pierre Ossmand408ca52016-04-29 14:26:05 +0200664 server.processSocketReadEvent(*i);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200665 if (FD_ISSET((*i)->getFd(), &wfds))
666 server.processSocketWriteEvent(*i);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000667 }
668
669 if (desktop.isRunning() && sched.goodTimeToPoll()) {
670 sched.newPass();
671 desktop.poll();
672 }
673 }
674
675 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000676 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000677 return 1;
678 }
679
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000680 TXWindow::handleXEvents(dpy);
681
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000682 vlog.info("Terminated");
683 return 0;
684}