blob: 025a09533502e738b0f2c68d56444d39526c5073 [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
Peter Korsgaard8fe50902017-07-13 00:35:57 +020055extern const unsigned short code_map_qnum_to_xorgevdev[];
56extern const unsigned int code_map_qnum_to_xorgevdev_len;
57
58extern const unsigned short code_map_qnum_to_xorgkbd[];
59extern const unsigned int code_map_qnum_to_xorgkbd_len;
60
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000061// XXX Lynx/OS 2.3: protos for select(), bzero()
62#ifdef Lynx
63#include <sys/proto.h>
64#endif
65
Constantin Kaplinskyb9632702006-12-01 10:54:55 +000066extern char buildtime[];
67
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000068using namespace rfb;
69using namespace network;
70
Peter Korsgaard5b7ff372017-07-13 00:36:01 +020071// number of XKb indicator leds to handle
72static const int N_LEDS = 3;
73
74// order is important as it must match RFB extension
75static const char * ledNames[N_LEDS] = {
76 "Scroll Lock", "Num Lock", "Caps Lock"
77};
78
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000079static LogWriter vlog("Main");
80
81IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
82 "cycle; actual interval may be dynamically "
83 "adjusted to satisfy MaxProcessorUsage setting", 30);
84IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
85 "CPU time to be consumed", 35);
86BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
87BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
88 "IRIX or Solaris", true);
89StringParameter displayname("display", "The X display", "");
90IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
91IntParameter queryConnectTimeout("QueryConnectTimeout",
92 "Number of seconds to show the Accept Connection dialog before "
93 "rejecting the connection",
94 10);
95StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
Peter Korsgaard8fe50902017-07-13 00:35:57 +020096BoolParameter rawKeyboard("RawKeyboard",
97 "Send keyboard events straight through and "
98 "avoid mapping them to the current keyboard "
99 "layout", false);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000100
101//
102// Allow the main loop terminate itself gracefully on receiving a signal.
103//
104
105static bool caughtSignal = false;
106
107static void CleanupSignalHandler(int sig)
108{
109 caughtSignal = true;
110}
111
112
113class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
114 public QueryResultCallback {
115public:
116 QueryConnHandler(Display* dpy, VNCServerST* vs)
117 : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
118 ~QueryConnHandler() { delete queryConnectDialog; }
119
120 // -=- VNCServerST::QueryConnectionHandler interface
121 virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
122 const char* userName,
123 char** reason) {
124 if (queryConnectSock) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000125 *reason = strDup("Another connection is currently being queried.");
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000126 return VNCServerST::REJECT;
127 }
128 if (!userName) userName = "(anonymous)";
129 queryConnectSock = sock;
130 CharArray address(sock->getPeerAddress());
131 delete queryConnectDialog;
132 queryConnectDialog = new QueryConnectDialog(display, address.buf,
133 userName, queryConnectTimeout,
134 this);
135 queryConnectDialog->map();
136 return VNCServerST::PENDING;
137 }
138
139 // -=- QueryResultCallback interface
140 virtual void queryApproved() {
141 server->approveConnection(queryConnectSock, true, 0);
142 queryConnectSock = 0;
143 }
144 virtual void queryRejected() {
145 server->approveConnection(queryConnectSock, false,
146 "Connection rejected by local user");
147 queryConnectSock = 0;
148 }
149private:
150 Display* display;
151 VNCServerST* server;
152 QueryConnectDialog* queryConnectDialog;
153 network::Socket* queryConnectSock;
154};
155
156
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100157class XDesktop : public SDesktop, public TXGlobalEventHandler
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000158{
159public:
160 XDesktop(Display* dpy_, Geometry *geometry_)
Constantin Kaplinsky303433a2008-06-04 05:57:06 +0000161 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000162 oldButtonMask(0), haveXtest(false), haveDamage(false),
Peter Korsgaard8fe50902017-07-13 00:35:57 +0200163 maxButtons(0), running(false), ledMasks(), ledState(0),
164 codeMap(0), codeMapLen(0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000165 {
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200166 int major, minor;
167
168 int xkbOpcode, xkbErrorBase;
169
170 major = XkbMajorVersion;
171 minor = XkbMinorVersion;
172 if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
173 &xkbErrorBase, &major, &minor)) {
174 vlog.error("XKEYBOARD extension not present");
175 throw Exception();
176 }
177
178 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
179 XkbIndicatorStateNotifyMask);
180
181 // figure out bit masks for the indicators we are interested in
182 for (int i = 0; i < N_LEDS; i++) {
183 Atom a;
184 int shift;
185 Bool on;
186
187 a = XInternAtom(dpy, ledNames[i], True);
188 if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
189 continue;
190
191 ledMasks[i] = 1u << shift;
192 vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
193 if (on)
194 ledState |= 1u << i;
195 }
196
Peter Korsgaard8fe50902017-07-13 00:35:57 +0200197 // X11 unfortunately uses keyboard driver specific keycodes and provides no
198 // direct way to query this, so guess based on the keyboard mapping
199 XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
200 if (desc && desc->names) {
201 char *keycodes = XGetAtomName(dpy, desc->names->keycodes);
202
203 if (keycodes) {
204 if (strncmp("evdev", keycodes, strlen("evdev")) == 0) {
205 codeMap = code_map_qnum_to_xorgevdev;
206 codeMapLen = code_map_qnum_to_xorgevdev_len;
207 vlog.info("Using evdev codemap\n");
208 } else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) {
209 codeMap = code_map_qnum_to_xorgkbd;
210 codeMapLen = code_map_qnum_to_xorgkbd_len;
211 vlog.info("Using xorgkbd codemap\n");
212 } else {
213 vlog.info("Unknown keycode '%s', no codemap\n", keycodes);
214 }
215 XFree(keycodes);
216 } else {
217 vlog.debug("Unable to get keycode map\n");
218 }
219
220 XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
221 }
222
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000223#ifdef HAVE_XTEST
224 int xtestEventBase;
225 int xtestErrorBase;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000226
227 if (XTestQueryExtension(dpy, &xtestEventBase,
228 &xtestErrorBase, &major, &minor)) {
229 XTestGrabControl(dpy, True);
230 vlog.info("XTest extension present - version %d.%d",major,minor);
231 haveXtest = true;
232 } else {
233#endif
234 vlog.info("XTest extension not present");
235 vlog.info("Unable to inject events or display while server is grabbed");
236#ifdef HAVE_XTEST
237 }
238#endif
239
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000240#ifdef HAVE_XDAMAGE
241 int xdamageErrorBase;
242
243 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000244 haveDamage = true;
245 } else {
246#endif
247 vlog.info("DAMAGE extension not present");
248 vlog.info("Will have to poll screen for changes");
249#ifdef HAVE_XDAMAGE
250 }
251#endif
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200252
253 TXWindow::setGlobalEventHandler(this);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000254 }
255 virtual ~XDesktop() {
256 stop();
257 }
258
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000259 inline void poll() {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000260 if (pb and not haveDamage)
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000261 pb->poll(server);
262 }
263
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000264 // -=- SDesktop interface
265
266 virtual void start(VNCServer* vs) {
267
268 // Determine actual number of buttons of the X pointer device.
269 unsigned char btnMap[8];
270 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
271 maxButtons = (numButtons > 8) ? 8 : numButtons;
272 vlog.info("Enabling %d button%s of X pointer device",
273 maxButtons, (maxButtons != 1) ? "s" : "");
274
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000275 // Create an ImageFactory instance for producing Image objects.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000276 ImageFactory factory((bool)useShm, (bool)useOverlay);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000277
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000278 // Create pixel buffer and provide it to the server object.
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100279 pb = new XPixelBuffer(dpy, factory, geometry->getRect());
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000280 vlog.info("Allocated %s", pb->getImage()->classDesc());
281
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000282 server = (VNCServerST *)vs;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000283 server->setPixelBuffer(pb);
284
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000285#ifdef HAVE_XDAMAGE
286 if (haveDamage) {
287 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
288 XDamageReportRawRectangles);
289 }
290#endif
291
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200292 server->setLEDState(ledState);
293
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000294 running = true;
295 }
296
297 virtual void stop() {
298 running = false;
299
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000300#ifdef HAVE_XDAMAGE
301 if (haveDamage)
302 XDamageDestroy(dpy, damage);
303#endif
304
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000305 delete pb;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000306 pb = 0;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000307 }
308
309 inline bool isRunning() {
310 return running;
311 }
312
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000313 virtual void pointerEvent(const Point& pos, int buttonMask) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000314#ifdef HAVE_XTEST
315 if (!haveXtest) return;
316 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
317 geometry->offsetLeft() + pos.x,
318 geometry->offsetTop() + pos.y,
319 CurrentTime);
320 if (buttonMask != oldButtonMask) {
321 for (int i = 0; i < maxButtons; i++) {
322 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
323 if (buttonMask & (1<<i)) {
324 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
325 } else {
326 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
327 }
328 }
329 }
330 }
331 oldButtonMask = buttonMask;
332#endif
333 }
334
Pierre Ossmanac730382017-08-16 15:20:20 +0200335#ifdef HAVE_XTEST
336 KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
337 XkbDescPtr xkb;
338 XkbStateRec state;
339 unsigned keycode;
340
341 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
342 if (!xkb)
343 return 0;
344
345 XkbGetState(dpy, XkbUseCoreKbd, &state);
346
347 for (keycode = xkb->min_key_code;
348 keycode <= xkb->max_key_code;
349 keycode++) {
350 KeySym cursym;
351 unsigned int mods;
352 XkbTranslateKeyCode(xkb, keycode, state.compat_state, &mods, &cursym);
353 if (cursym == keysym)
354 break;
355 }
356
357 if (keycode > xkb->max_key_code)
358 keycode = 0;
359
360 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
361
362 return keycode;
363 }
364#endif
365
Pierre Ossman5ae28212017-05-16 14:30:38 +0200366 virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000367#ifdef HAVE_XTEST
Peter Korsgaard8fe50902017-07-13 00:35:57 +0200368 int keycode = 0;
Pierre Ossmanac730382017-08-16 15:20:20 +0200369
370 if (!haveXtest)
371 return;
Peter Korsgaard8fe50902017-07-13 00:35:57 +0200372
373 // Use scan code if provided and mapping exists
374 if (codeMap && rawKeyboard && xtcode < codeMapLen)
375 keycode = codeMap[xtcode];
376
Pierre Ossman63cc07e2017-09-15 11:17:02 +0200377 if (!keycode) {
378 if (!down || (pressedKeys.find(keysym) != pressedKeys.end()))
379 keycode = pressedKeys[keysym];
Pierre Ossmanac730382017-08-16 15:20:20 +0200380 else {
381 // XKeysymToKeycode() doesn't respect state, so we have to use
382 // something slightly more complex
Pierre Ossman63cc07e2017-09-15 11:17:02 +0200383 keycode = XkbKeysymToKeycode(dpy, keysym);
Pierre Ossmanac730382017-08-16 15:20:20 +0200384 }
Pierre Ossmanac730382017-08-16 15:20:20 +0200385 }
Peter Korsgaard8fe50902017-07-13 00:35:57 +0200386
Pierre Ossman63cc07e2017-09-15 11:17:02 +0200387 if (!keycode)
388 return;
389
390 if (down)
391 pressedKeys[keysym] = keycode;
392 else
393 pressedKeys.erase(keysym);
394
Pierre Ossmanac730382017-08-16 15:20:20 +0200395 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000396#endif
397 }
398
399 virtual void clientCutText(const char* str, int len) {
400 }
401
402 virtual Point getFbSize() {
403 return Point(pb->width(), pb->height());
404 }
405
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000406 // -=- TXGlobalEventHandler interface
407
408 virtual bool handleGlobalEvent(XEvent* ev) {
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200409 if (ev->type == xkbEventBase + XkbEventCode) {
410 XkbEvent *kb = (XkbEvent *)ev;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000411
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200412 if (kb->any.xkb_type != XkbIndicatorStateNotify)
413 return false;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000414
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200415 vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
416
417 ledState = 0;
418 for (int i = 0; i < N_LEDS; i++) {
419 if (kb->indicators.state & ledMasks[i])
420 ledState |= 1u << i;
421 }
422
423 if (running)
424 server->setLEDState(ledState);
425
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000426 return true;
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200427#ifdef HAVE_XDAMAGE
428 } else if (ev->type == xdamageEventBase) {
429 XDamageNotifyEvent* dev;
430 Rect rect;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000431
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200432 if (!running)
433 return true;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000434
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200435 dev = (XDamageNotifyEvent*)ev;
436 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
437 server->add_changed(rect);
438
439 return true;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000440#endif
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200441 }
442
443 return false;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000444 }
445
446protected:
447 Display* dpy;
448 Geometry* geometry;
Constantin Kaplinsky2c019832008-05-30 11:02:04 +0000449 XPixelBuffer* pb;
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000450 VNCServerST* server;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000451 int oldButtonMask;
452 bool haveXtest;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000453 bool haveDamage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000454 int maxButtons;
Pierre Ossmanac730382017-08-16 15:20:20 +0200455 std::map<KeySym, KeyCode> pressedKeys;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000456 bool running;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000457#ifdef HAVE_XDAMAGE
458 Damage damage;
459 int xdamageEventBase;
460#endif
Peter Korsgaard5b7ff372017-07-13 00:36:01 +0200461 int xkbEventBase;
462 int ledMasks[N_LEDS];
463 unsigned ledState;
Peter Korsgaard8fe50902017-07-13 00:35:57 +0200464 const unsigned short *codeMap;
465 unsigned codeMapLen;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000466};
467
468
469class FileTcpFilter : public TcpFilter
470{
471
472public:
473
474 FileTcpFilter(const char *fname)
475 : TcpFilter("-"), fileName(NULL), lastModTime(0)
476 {
477 if (fname != NULL)
478 fileName = strdup((char *)fname);
479 }
480
481 virtual ~FileTcpFilter()
482 {
483 if (fileName != NULL)
484 free(fileName);
485 }
486
487 virtual bool verifyConnection(Socket* s)
488 {
489 if (!reloadRules()) {
490 vlog.error("Could not read IP filtering rules: rejecting all clients");
491 filter.clear();
492 filter.push_back(parsePattern("-"));
493 return false;
494 }
495
496 return TcpFilter::verifyConnection(s);
497 }
498
499protected:
500
501 bool reloadRules()
502 {
503 if (fileName == NULL)
504 return true;
505
506 struct stat st;
507 if (stat(fileName, &st) != 0)
508 return false;
509
510 if (st.st_mtime != lastModTime) {
511 // Actually reload only if the file was modified
512 FILE *fp = fopen(fileName, "r");
513 if (fp == NULL)
514 return false;
515
516 // Remove all the rules from the parent class
517 filter.clear();
518
519 // Parse the file contents adding rules to the parent class
520 char buf[32];
521 while (readLine(buf, 32, fp)) {
522 if (buf[0] && strchr("+-?", buf[0])) {
523 filter.push_back(parsePattern(buf));
524 }
525 }
526
527 fclose(fp);
528 lastModTime = st.st_mtime;
529 }
530 return true;
531 }
532
533protected:
534
535 char *fileName;
536 time_t lastModTime;
537
538private:
539
540 //
541 // NOTE: we silently truncate long lines in this function.
542 //
543
544 bool readLine(char *buf, int bufSize, FILE *fp)
545 {
546 if (fp == NULL || buf == NULL || bufSize == 0)
547 return false;
548
549 if (fgets(buf, bufSize, fp) == NULL)
550 return false;
551
552 char *ptr = strchr(buf, '\n');
553 if (ptr != NULL) {
554 *ptr = '\0'; // remove newline at the end
555 } else {
556 if (!feof(fp)) {
557 int c;
558 do { // skip the rest of a long line
559 c = getc(fp);
560 } while (c != '\n' && c != EOF);
561 }
562 }
563 return true;
564 }
565
566};
567
568char* programName;
569
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000570static void printVersion(FILE *fp)
571{
Peter Ã…strand4eacc022009-02-27 10:12:14 +0000572 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000573 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000574}
575
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000576static void usage()
577{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000578 printVersion(stderr);
579 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
580 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000581 fprintf(stderr,"\n"
582 "Parameters can be turned on with -<param> or off with -<param>=0\n"
583 "Parameters which take a value can be specified as "
584 "-<param> <value>\n"
585 "Other valid forms are <param>=<value> -<param>=<value> "
586 "--<param>=<value>\n"
587 "Parameter names are case-insensitive. The parameters are:\n\n");
588 Configuration::listParams(79, 14);
589 exit(1);
590}
591
592int main(int argc, char** argv)
593{
594 initStdIOLoggers();
595 LogWriter::setLogParams("*:stderr:30");
596
597 programName = argv[0];
598 Display* dpy;
599
Adam Tkacc58b3d12010-04-23 13:55:10 +0000600 Configuration::enableServerParams();
601
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000602 for (int i = 1; i < argc; i++) {
603 if (Configuration::setParam(argv[i]))
604 continue;
605
606 if (argv[i][0] == '-') {
607 if (i+1 < argc) {
608 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
609 i++;
610 continue;
611 }
612 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000613 if (strcmp(argv[i], "-v") == 0 ||
614 strcmp(argv[i], "-version") == 0 ||
615 strcmp(argv[i], "--version") == 0) {
616 printVersion(stdout);
617 return 0;
618 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000619 usage();
620 }
621
622 usage();
623 }
624
625 CharArray dpyStr(displayname.getData());
626 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000627 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000628 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000629 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000630 exit(1);
631 }
632
633 signal(SIGHUP, CleanupSignalHandler);
634 signal(SIGINT, CleanupSignalHandler);
635 signal(SIGTERM, CleanupSignalHandler);
636
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200637 std::list<TcpListener*> listeners;
Tim Waugh892d10a2015-03-11 13:12:07 +0000638
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000639 try {
640 TXWindow::init(dpy,"x0vncserver");
641 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
642 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000643 if (geo.getRect().is_empty()) {
644 vlog.error("Exiting with error");
645 return 1;
646 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000647 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000648
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000649 VNCServerST server("x0vncserver", &desktop);
650 QueryConnHandler qcHandler(dpy, &server);
651 server.setQueryConnectionHandler(&qcHandler);
652
Tim Waugh892d10a2015-03-11 13:12:07 +0000653 createTcpListeners(&listeners, 0, (int)rfbport);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000654 vlog.info("Listening on port %d", (int)rfbport);
655
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000656 const char *hostsData = hostsFile.getData();
657 FileTcpFilter fileTcpFilter(hostsData);
658 if (strlen(hostsData) != 0)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200659 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000660 i != listeners.end();
661 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200662 (*i)->setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000663 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000664
665 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
666
667 while (!caughtSignal) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200668 int wait_ms;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000669 struct timeval tv;
Pierre Ossman16419cc2016-04-29 14:29:43 +0200670 fd_set rfds, wfds;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000671 std::list<Socket*> sockets;
672 std::list<Socket*>::iterator i;
673
674 // Process any incoming X events
675 TXWindow::handleXEvents(dpy);
676
677 FD_ZERO(&rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200678 FD_ZERO(&wfds);
679
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000680 FD_SET(ConnectionNumber(dpy), &rfds);
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200681 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000682 i != listeners.end();
683 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200684 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000685
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000686 server.getSockets(&sockets);
687 int clients_connected = 0;
688 for (i = sockets.begin(); i != sockets.end(); i++) {
689 if ((*i)->isShutdown()) {
690 server.removeSocket(*i);
691 delete (*i);
692 } else {
693 FD_SET((*i)->getFd(), &rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200694 if ((*i)->outStream().bufferUsage() > 0)
695 FD_SET((*i)->getFd(), &wfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000696 clients_connected++;
697 }
698 }
699
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000700 if (!clients_connected)
701 sched.reset();
702
Pierre Ossman278e4202016-04-29 14:28:54 +0200703 wait_ms = 0;
704
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000705 if (sched.isRunning()) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200706 wait_ms = sched.millisRemaining();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000707 if (wait_ms > 500) {
708 wait_ms = 500;
709 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000710 }
Pierre Ossman278e4202016-04-29 14:28:54 +0200711
712 soonestTimeout(&wait_ms, server.checkTimeouts());
713
714 tv.tv_sec = wait_ms / 1000;
715 tv.tv_usec = (wait_ms % 1000) * 1000;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000716
717 // Do the wait...
718 sched.sleepStarted();
Pierre Ossman16419cc2016-04-29 14:29:43 +0200719 int n = select(FD_SETSIZE, &rfds, &wfds, 0,
Pierre Ossman278e4202016-04-29 14:28:54 +0200720 wait_ms ? &tv : NULL);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000721 sched.sleepFinished();
722
723 if (n < 0) {
724 if (errno == EINTR) {
725 vlog.debug("Interrupted select() system call");
726 continue;
727 } else {
728 throw rdr::SystemException("select", errno);
729 }
730 }
731
732 // Accept new VNC connections
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200733 for (std::list<TcpListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000734 i != listeners.end();
735 i++) {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200736 if (FD_ISSET((*i)->getFd(), &rfds)) {
737 Socket* sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000738 if (sock) {
Pierre Ossman16419cc2016-04-29 14:29:43 +0200739 sock->outStream().setBlocking(false);
Tim Waugh892d10a2015-03-11 13:12:07 +0000740 server.addSocket(sock);
741 } else {
742 vlog.status("Client connection rejected");
743 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000744 }
745 }
746
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000747 server.checkTimeouts();
748
749 // Client list could have been changed.
750 server.getSockets(&sockets);
751
752 // Nothing more to do if there are no client connections.
753 if (sockets.empty())
754 continue;
755
756 // Process events on existing VNC connections
757 for (i = sockets.begin(); i != sockets.end(); i++) {
758 if (FD_ISSET((*i)->getFd(), &rfds))
Pierre Ossmand408ca52016-04-29 14:26:05 +0200759 server.processSocketReadEvent(*i);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200760 if (FD_ISSET((*i)->getFd(), &wfds))
761 server.processSocketWriteEvent(*i);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000762 }
763
764 if (desktop.isRunning() && sched.goodTimeToPoll()) {
765 sched.newPass();
766 desktop.poll();
767 }
768 }
769
770 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000771 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000772 return 1;
773 }
774
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000775 TXWindow::handleXEvents(dpy);
776
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000777 vlog.info("Terminated");
778 return 0;
779}