blob: 34713ea855523e4255c5f605b11c6fd5051c0856 [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>
Pierre Ossmanac730382017-08-16 15:20:20 +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
65static LogWriter vlog("Main");
66
67IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
68 "cycle; actual interval may be dynamically "
69 "adjusted to satisfy MaxProcessorUsage setting", 30);
70IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
71 "CPU time to be consumed", 35);
72BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
73BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
74 "IRIX or Solaris", true);
75StringParameter displayname("display", "The X display", "");
76IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
77IntParameter queryConnectTimeout("QueryConnectTimeout",
78 "Number of seconds to show the Accept Connection dialog before "
79 "rejecting the connection",
80 10);
81StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
82
83//
84// Allow the main loop terminate itself gracefully on receiving a signal.
85//
86
87static bool caughtSignal = false;
88
89static void CleanupSignalHandler(int sig)
90{
91 caughtSignal = true;
92}
93
94
95class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
96 public QueryResultCallback {
97public:
98 QueryConnHandler(Display* dpy, VNCServerST* vs)
99 : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
100 ~QueryConnHandler() { delete queryConnectDialog; }
101
102 // -=- VNCServerST::QueryConnectionHandler interface
103 virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
104 const char* userName,
105 char** reason) {
106 if (queryConnectSock) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000107 *reason = strDup("Another connection is currently being queried.");
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000108 return VNCServerST::REJECT;
109 }
110 if (!userName) userName = "(anonymous)";
111 queryConnectSock = sock;
112 CharArray address(sock->getPeerAddress());
113 delete queryConnectDialog;
114 queryConnectDialog = new QueryConnectDialog(display, address.buf,
115 userName, queryConnectTimeout,
116 this);
117 queryConnectDialog->map();
118 return VNCServerST::PENDING;
119 }
120
121 // -=- QueryResultCallback interface
122 virtual void queryApproved() {
123 server->approveConnection(queryConnectSock, true, 0);
124 queryConnectSock = 0;
125 }
126 virtual void queryRejected() {
127 server->approveConnection(queryConnectSock, false,
128 "Connection rejected by local user");
129 queryConnectSock = 0;
130 }
131private:
132 Display* display;
133 VNCServerST* server;
134 QueryConnectDialog* queryConnectDialog;
135 network::Socket* queryConnectSock;
136};
137
138
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100139class XDesktop : public SDesktop, public TXGlobalEventHandler
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000140{
141public:
142 XDesktop(Display* dpy_, Geometry *geometry_)
Constantin Kaplinsky303433a2008-06-04 05:57:06 +0000143 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000144 oldButtonMask(0), haveXtest(false), haveDamage(false),
145 maxButtons(0), running(false)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000146 {
147#ifdef HAVE_XTEST
148 int xtestEventBase;
149 int xtestErrorBase;
150 int major, minor;
151
152 if (XTestQueryExtension(dpy, &xtestEventBase,
153 &xtestErrorBase, &major, &minor)) {
154 XTestGrabControl(dpy, True);
155 vlog.info("XTest extension present - version %d.%d",major,minor);
156 haveXtest = true;
157 } else {
158#endif
159 vlog.info("XTest extension not present");
160 vlog.info("Unable to inject events or display while server is grabbed");
161#ifdef HAVE_XTEST
162 }
163#endif
164
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000165#ifdef HAVE_XDAMAGE
166 int xdamageErrorBase;
167
168 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
169 TXWindow::setGlobalEventHandler(this);
170 haveDamage = true;
171 } else {
172#endif
173 vlog.info("DAMAGE extension not present");
174 vlog.info("Will have to poll screen for changes");
175#ifdef HAVE_XDAMAGE
176 }
177#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000178 }
179 virtual ~XDesktop() {
180 stop();
181 }
182
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000183 inline void poll() {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000184 if (pb and not haveDamage)
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000185 pb->poll(server);
186 }
187
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000188 // -=- SDesktop interface
189
190 virtual void start(VNCServer* vs) {
191
192 // Determine actual number of buttons of the X pointer device.
193 unsigned char btnMap[8];
194 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
195 maxButtons = (numButtons > 8) ? 8 : numButtons;
196 vlog.info("Enabling %d button%s of X pointer device",
197 maxButtons, (maxButtons != 1) ? "s" : "");
198
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000199 // Create an ImageFactory instance for producing Image objects.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000200 ImageFactory factory((bool)useShm, (bool)useOverlay);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000201
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000202 // Create pixel buffer and provide it to the server object.
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100203 pb = new XPixelBuffer(dpy, factory, geometry->getRect());
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000204 vlog.info("Allocated %s", pb->getImage()->classDesc());
205
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000206 server = (VNCServerST *)vs;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000207 server->setPixelBuffer(pb);
208
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000209#ifdef HAVE_XDAMAGE
210 if (haveDamage) {
211 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
212 XDamageReportRawRectangles);
213 }
214#endif
215
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000216 running = true;
217 }
218
219 virtual void stop() {
220 running = false;
221
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000222#ifdef HAVE_XDAMAGE
223 if (haveDamage)
224 XDamageDestroy(dpy, damage);
225#endif
226
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000227 delete pb;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000228 pb = 0;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000229 }
230
231 inline bool isRunning() {
232 return running;
233 }
234
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000235 virtual void pointerEvent(const Point& pos, int buttonMask) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000236#ifdef HAVE_XTEST
237 if (!haveXtest) return;
238 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
239 geometry->offsetLeft() + pos.x,
240 geometry->offsetTop() + pos.y,
241 CurrentTime);
242 if (buttonMask != oldButtonMask) {
243 for (int i = 0; i < maxButtons; i++) {
244 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
245 if (buttonMask & (1<<i)) {
246 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
247 } else {
248 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
249 }
250 }
251 }
252 }
253 oldButtonMask = buttonMask;
254#endif
255 }
256
Pierre Ossmanac730382017-08-16 15:20:20 +0200257#ifdef HAVE_XTEST
258 KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
259 XkbDescPtr xkb;
260 XkbStateRec state;
261 unsigned keycode;
262
263 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
264 if (!xkb)
265 return 0;
266
267 XkbGetState(dpy, XkbUseCoreKbd, &state);
268
269 for (keycode = xkb->min_key_code;
270 keycode <= xkb->max_key_code;
271 keycode++) {
272 KeySym cursym;
273 unsigned int mods;
274 XkbTranslateKeyCode(xkb, keycode, state.compat_state, &mods, &cursym);
275 if (cursym == keysym)
276 break;
277 }
278
279 if (keycode > xkb->max_key_code)
280 keycode = 0;
281
282 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
283
284 return keycode;
285 }
286#endif
287
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000288 virtual void keyEvent(rdr::U32 key, bool down) {
289#ifdef HAVE_XTEST
Pierre Ossmanac730382017-08-16 15:20:20 +0200290 int keycode;
291
292 if (!haveXtest)
293 return;
294
295 if (down) {
296 if (pressedKeys.find(key) != pressedKeys.end())
297 keycode = pressedKeys[key];
298 else {
299 // XKeysymToKeycode() doesn't respect state, so we have to use
300 // something slightly more complex
301 keycode = XkbKeysymToKeycode(dpy, key);
302 if (!keycode)
303 return;
304 pressedKeys[key] = keycode;
305 }
306 } else {
307 keycode = pressedKeys[key];
308 pressedKeys.erase(key);
309 }
310
311 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000312#endif
313 }
314
315 virtual void clientCutText(const char* str, int len) {
316 }
317
318 virtual Point getFbSize() {
319 return Point(pb->width(), pb->height());
320 }
321
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000322 // -=- TXGlobalEventHandler interface
323
324 virtual bool handleGlobalEvent(XEvent* ev) {
325#ifdef HAVE_XDAMAGE
326 XDamageNotifyEvent* dev;
327 Rect rect;
328
329 if (ev->type != xdamageEventBase)
330 return false;
331
332 if (!running)
333 return true;
334
335 dev = (XDamageNotifyEvent*)ev;
336 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
337 server->add_changed(rect);
338
339 return true;
Pierre Ossman25ae7e32015-03-03 16:45:18 +0100340#else
341 return false;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000342#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000343 }
344
345protected:
346 Display* dpy;
347 Geometry* geometry;
Constantin Kaplinsky2c019832008-05-30 11:02:04 +0000348 XPixelBuffer* pb;
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000349 VNCServerST* server;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000350 int oldButtonMask;
351 bool haveXtest;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000352 bool haveDamage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000353 int maxButtons;
Pierre Ossmanac730382017-08-16 15:20:20 +0200354 std::map<KeySym, KeyCode> pressedKeys;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000355 bool running;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000356#ifdef HAVE_XDAMAGE
357 Damage damage;
358 int xdamageEventBase;
359#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000360};
361
362
363class FileTcpFilter : public TcpFilter
364{
365
366public:
367
368 FileTcpFilter(const char *fname)
369 : TcpFilter("-"), fileName(NULL), lastModTime(0)
370 {
371 if (fname != NULL)
372 fileName = strdup((char *)fname);
373 }
374
375 virtual ~FileTcpFilter()
376 {
377 if (fileName != NULL)
378 free(fileName);
379 }
380
381 virtual bool verifyConnection(Socket* s)
382 {
383 if (!reloadRules()) {
384 vlog.error("Could not read IP filtering rules: rejecting all clients");
385 filter.clear();
386 filter.push_back(parsePattern("-"));
387 return false;
388 }
389
390 return TcpFilter::verifyConnection(s);
391 }
392
393protected:
394
395 bool reloadRules()
396 {
397 if (fileName == NULL)
398 return true;
399
400 struct stat st;
401 if (stat(fileName, &st) != 0)
402 return false;
403
404 if (st.st_mtime != lastModTime) {
405 // Actually reload only if the file was modified
406 FILE *fp = fopen(fileName, "r");
407 if (fp == NULL)
408 return false;
409
410 // Remove all the rules from the parent class
411 filter.clear();
412
413 // Parse the file contents adding rules to the parent class
414 char buf[32];
415 while (readLine(buf, 32, fp)) {
416 if (buf[0] && strchr("+-?", buf[0])) {
417 filter.push_back(parsePattern(buf));
418 }
419 }
420
421 fclose(fp);
422 lastModTime = st.st_mtime;
423 }
424 return true;
425 }
426
427protected:
428
429 char *fileName;
430 time_t lastModTime;
431
432private:
433
434 //
435 // NOTE: we silently truncate long lines in this function.
436 //
437
438 bool readLine(char *buf, int bufSize, FILE *fp)
439 {
440 if (fp == NULL || buf == NULL || bufSize == 0)
441 return false;
442
443 if (fgets(buf, bufSize, fp) == NULL)
444 return false;
445
446 char *ptr = strchr(buf, '\n');
447 if (ptr != NULL) {
448 *ptr = '\0'; // remove newline at the end
449 } else {
450 if (!feof(fp)) {
451 int c;
452 do { // skip the rest of a long line
453 c = getc(fp);
454 } while (c != '\n' && c != EOF);
455 }
456 }
457 return true;
458 }
459
460};
461
462char* programName;
463
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000464static void printVersion(FILE *fp)
465{
Peter Ã…strand4eacc022009-02-27 10:12:14 +0000466 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000467 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000468}
469
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000470static void usage()
471{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000472 printVersion(stderr);
473 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
474 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000475 fprintf(stderr,"\n"
476 "Parameters can be turned on with -<param> or off with -<param>=0\n"
477 "Parameters which take a value can be specified as "
478 "-<param> <value>\n"
479 "Other valid forms are <param>=<value> -<param>=<value> "
480 "--<param>=<value>\n"
481 "Parameter names are case-insensitive. The parameters are:\n\n");
482 Configuration::listParams(79, 14);
483 exit(1);
484}
485
486int main(int argc, char** argv)
487{
488 initStdIOLoggers();
489 LogWriter::setLogParams("*:stderr:30");
490
491 programName = argv[0];
492 Display* dpy;
493
Adam Tkacc58b3d12010-04-23 13:55:10 +0000494 Configuration::enableServerParams();
495
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000496 for (int i = 1; i < argc; i++) {
497 if (Configuration::setParam(argv[i]))
498 continue;
499
500 if (argv[i][0] == '-') {
501 if (i+1 < argc) {
502 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
503 i++;
504 continue;
505 }
506 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000507 if (strcmp(argv[i], "-v") == 0 ||
508 strcmp(argv[i], "-version") == 0 ||
509 strcmp(argv[i], "--version") == 0) {
510 printVersion(stdout);
511 return 0;
512 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000513 usage();
514 }
515
516 usage();
517 }
518
519 CharArray dpyStr(displayname.getData());
520 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000521 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000522 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000523 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000524 exit(1);
525 }
526
527 signal(SIGHUP, CleanupSignalHandler);
528 signal(SIGINT, CleanupSignalHandler);
529 signal(SIGTERM, CleanupSignalHandler);
530
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200531 std::list<TcpListener*> listeners;
Tim Waugh892d10a2015-03-11 13:12:07 +0000532
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000533 try {
534 TXWindow::init(dpy,"x0vncserver");
535 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
536 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000537 if (geo.getRect().is_empty()) {
538 vlog.error("Exiting with error");
539 return 1;
540 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000541 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000542
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000543 VNCServerST server("x0vncserver", &desktop);
544 QueryConnHandler qcHandler(dpy, &server);
545 server.setQueryConnectionHandler(&qcHandler);
546
Tim Waugh892d10a2015-03-11 13:12:07 +0000547 createTcpListeners(&listeners, 0, (int)rfbport);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000548 vlog.info("Listening on port %d", (int)rfbport);
549
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000550 const char *hostsData = hostsFile.getData();
551 FileTcpFilter fileTcpFilter(hostsData);
552 if (strlen(hostsData) != 0)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200553 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000554 i != listeners.end();
555 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200556 (*i)->setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000557 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000558
559 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
560
561 while (!caughtSignal) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200562 int wait_ms;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000563 struct timeval tv;
Pierre Ossman16419cc2016-04-29 14:29:43 +0200564 fd_set rfds, wfds;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000565 std::list<Socket*> sockets;
566 std::list<Socket*>::iterator i;
567
568 // Process any incoming X events
569 TXWindow::handleXEvents(dpy);
570
571 FD_ZERO(&rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200572 FD_ZERO(&wfds);
573
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000574 FD_SET(ConnectionNumber(dpy), &rfds);
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200575 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000576 i != listeners.end();
577 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200578 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000579
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000580 server.getSockets(&sockets);
581 int clients_connected = 0;
582 for (i = sockets.begin(); i != sockets.end(); i++) {
583 if ((*i)->isShutdown()) {
584 server.removeSocket(*i);
585 delete (*i);
586 } else {
587 FD_SET((*i)->getFd(), &rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200588 if ((*i)->outStream().bufferUsage() > 0)
589 FD_SET((*i)->getFd(), &wfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000590 clients_connected++;
591 }
592 }
593
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000594 if (!clients_connected)
595 sched.reset();
596
Pierre Ossman278e4202016-04-29 14:28:54 +0200597 wait_ms = 0;
598
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000599 if (sched.isRunning()) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200600 wait_ms = sched.millisRemaining();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000601 if (wait_ms > 500) {
602 wait_ms = 500;
603 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000604 }
Pierre Ossman278e4202016-04-29 14:28:54 +0200605
606 soonestTimeout(&wait_ms, server.checkTimeouts());
607
608 tv.tv_sec = wait_ms / 1000;
609 tv.tv_usec = (wait_ms % 1000) * 1000;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000610
611 // Do the wait...
612 sched.sleepStarted();
Pierre Ossman16419cc2016-04-29 14:29:43 +0200613 int n = select(FD_SETSIZE, &rfds, &wfds, 0,
Pierre Ossman278e4202016-04-29 14:28:54 +0200614 wait_ms ? &tv : NULL);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000615 sched.sleepFinished();
616
617 if (n < 0) {
618 if (errno == EINTR) {
619 vlog.debug("Interrupted select() system call");
620 continue;
621 } else {
622 throw rdr::SystemException("select", errno);
623 }
624 }
625
626 // Accept new VNC connections
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200627 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000628 i != listeners.end();
629 i++) {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200630 if (FD_ISSET((*i)->getFd(), &rfds)) {
631 Socket* sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000632 if (sock) {
Pierre Ossman16419cc2016-04-29 14:29:43 +0200633 sock->outStream().setBlocking(false);
Tim Waugh892d10a2015-03-11 13:12:07 +0000634 server.addSocket(sock);
635 } else {
636 vlog.status("Client connection rejected");
637 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000638 }
639 }
640
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000641 server.checkTimeouts();
642
643 // Client list could have been changed.
644 server.getSockets(&sockets);
645
646 // Nothing more to do if there are no client connections.
647 if (sockets.empty())
648 continue;
649
650 // Process events on existing VNC connections
651 for (i = sockets.begin(); i != sockets.end(); i++) {
652 if (FD_ISSET((*i)->getFd(), &rfds))
Pierre Ossmand408ca52016-04-29 14:26:05 +0200653 server.processSocketReadEvent(*i);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200654 if (FD_ISSET((*i)->getFd(), &wfds))
655 server.processSocketWriteEvent(*i);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000656 }
657
658 if (desktop.isRunning() && sched.goodTimeToPoll()) {
659 sched.newPass();
660 desktop.poll();
661 }
662 }
663
664 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000665 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000666 return 1;
667 }
668
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000669 TXWindow::handleXEvents(dpy);
670
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000671 vlog.info("Terminated");
672 return 0;
673}