blob: eac9edfbcbb8783c81d74432ca0329ec0aa53d76 [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Constantin Kaplinsky2c019832008-05-30 11:02:04 +00002 * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20// FIXME: Check cases when screen width/height is not a multiply of 32.
21// e.g. 800x600.
22
23#include <strings.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <errno.h>
28#include <rfb/Logger_stdio.h>
29#include <rfb/LogWriter.h>
30#include <rfb/VNCServerST.h>
31#include <rfb/Configuration.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000032#include <rfb/Timer.h>
33#include <network/TcpSocket.h>
Pierre Ossman39594b82018-05-04 15:40:22 +020034#include <network/UnixSocket.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000035
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000036#include <signal.h>
37#include <X11/X.h>
38#include <X11/Xlib.h>
39#include <X11/Xutil.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000040
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020041#include <x0vncserver/XDesktop.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000042#include <x0vncserver/Geometry.h>
43#include <x0vncserver/Image.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000044#include <x0vncserver/PollingScheduler.h>
45
Constantin Kaplinskyb9632702006-12-01 10:54:55 +000046extern char buildtime[];
47
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000048using namespace rfb;
49using namespace network;
50
51static LogWriter vlog("Main");
52
53IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
54 "cycle; actual interval may be dynamically "
55 "adjusted to satisfy MaxProcessorUsage setting", 30);
56IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
57 "CPU time to be consumed", 35);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000058StringParameter displayname("display", "The X display", "");
59IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
Pierre Ossman39594b82018-05-04 15:40:22 +020060StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
61IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000062StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
63
64//
65// Allow the main loop terminate itself gracefully on receiving a signal.
66//
67
68static bool caughtSignal = false;
69
70static void CleanupSignalHandler(int sig)
71{
72 caughtSignal = true;
73}
74
75
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000076class FileTcpFilter : public TcpFilter
77{
78
79public:
80
81 FileTcpFilter(const char *fname)
82 : TcpFilter("-"), fileName(NULL), lastModTime(0)
83 {
84 if (fname != NULL)
85 fileName = strdup((char *)fname);
86 }
87
88 virtual ~FileTcpFilter()
89 {
90 if (fileName != NULL)
91 free(fileName);
92 }
93
94 virtual bool verifyConnection(Socket* s)
95 {
96 if (!reloadRules()) {
97 vlog.error("Could not read IP filtering rules: rejecting all clients");
98 filter.clear();
99 filter.push_back(parsePattern("-"));
100 return false;
101 }
102
103 return TcpFilter::verifyConnection(s);
104 }
105
106protected:
107
108 bool reloadRules()
109 {
110 if (fileName == NULL)
111 return true;
112
113 struct stat st;
114 if (stat(fileName, &st) != 0)
115 return false;
116
117 if (st.st_mtime != lastModTime) {
118 // Actually reload only if the file was modified
119 FILE *fp = fopen(fileName, "r");
120 if (fp == NULL)
121 return false;
122
123 // Remove all the rules from the parent class
124 filter.clear();
125
126 // Parse the file contents adding rules to the parent class
127 char buf[32];
128 while (readLine(buf, 32, fp)) {
129 if (buf[0] && strchr("+-?", buf[0])) {
130 filter.push_back(parsePattern(buf));
131 }
132 }
133
134 fclose(fp);
135 lastModTime = st.st_mtime;
136 }
137 return true;
138 }
139
140protected:
141
142 char *fileName;
143 time_t lastModTime;
144
145private:
146
147 //
148 // NOTE: we silently truncate long lines in this function.
149 //
150
151 bool readLine(char *buf, int bufSize, FILE *fp)
152 {
153 if (fp == NULL || buf == NULL || bufSize == 0)
154 return false;
155
156 if (fgets(buf, bufSize, fp) == NULL)
157 return false;
158
159 char *ptr = strchr(buf, '\n');
160 if (ptr != NULL) {
161 *ptr = '\0'; // remove newline at the end
162 } else {
163 if (!feof(fp)) {
164 int c;
165 do { // skip the rest of a long line
166 c = getc(fp);
167 } while (c != '\n' && c != EOF);
168 }
169 }
170 return true;
171 }
172
173};
174
175char* programName;
176
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000177static void printVersion(FILE *fp)
178{
Peter Åstrand4eacc022009-02-27 10:12:14 +0000179 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000180 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000181}
182
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000183static void usage()
184{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000185 printVersion(stderr);
186 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
187 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000188 fprintf(stderr,"\n"
189 "Parameters can be turned on with -<param> or off with -<param>=0\n"
190 "Parameters which take a value can be specified as "
191 "-<param> <value>\n"
192 "Other valid forms are <param>=<value> -<param>=<value> "
193 "--<param>=<value>\n"
194 "Parameter names are case-insensitive. The parameters are:\n\n");
195 Configuration::listParams(79, 14);
196 exit(1);
197}
198
199int main(int argc, char** argv)
200{
201 initStdIOLoggers();
202 LogWriter::setLogParams("*:stderr:30");
203
204 programName = argv[0];
205 Display* dpy;
206
Adam Tkacc58b3d12010-04-23 13:55:10 +0000207 Configuration::enableServerParams();
208
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000209 for (int i = 1; i < argc; i++) {
210 if (Configuration::setParam(argv[i]))
211 continue;
212
213 if (argv[i][0] == '-') {
214 if (i+1 < argc) {
215 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
216 i++;
217 continue;
218 }
219 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000220 if (strcmp(argv[i], "-v") == 0 ||
221 strcmp(argv[i], "-version") == 0 ||
222 strcmp(argv[i], "--version") == 0) {
223 printVersion(stdout);
224 return 0;
225 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000226 usage();
227 }
228
229 usage();
230 }
231
232 CharArray dpyStr(displayname.getData());
233 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000234 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000235 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000236 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000237 exit(1);
238 }
239
240 signal(SIGHUP, CleanupSignalHandler);
241 signal(SIGINT, CleanupSignalHandler);
242 signal(SIGTERM, CleanupSignalHandler);
243
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200244 std::list<SocketListener*> listeners;
Tim Waugh892d10a2015-03-11 13:12:07 +0000245
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000246 try {
247 TXWindow::init(dpy,"x0vncserver");
248 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
249 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000250 if (geo.getRect().is_empty()) {
251 vlog.error("Exiting with error");
252 return 1;
253 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000254 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000255
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000256 VNCServerST server("x0vncserver", &desktop);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000257
Pierre Ossman39594b82018-05-04 15:40:22 +0200258 if (rfbunixpath.getValueStr()[0] != '\0') {
259 listeners.push_back(new network::UnixListener(rfbunixpath, rfbunixmode));
260 vlog.info("Listening on %s (mode %04o)", (const char*)rfbunixpath, (int)rfbunixmode);
261 } else {
262 createTcpListeners(&listeners, 0, (int)rfbport);
263 vlog.info("Listening on port %d", (int)rfbport);
264 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000265
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000266 const char *hostsData = hostsFile.getData();
267 FileTcpFilter fileTcpFilter(hostsData);
268 if (strlen(hostsData) != 0)
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200269 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000270 i != listeners.end();
271 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200272 (*i)->setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000273 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000274
275 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
276
277 while (!caughtSignal) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200278 int wait_ms;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000279 struct timeval tv;
Pierre Ossman16419cc2016-04-29 14:29:43 +0200280 fd_set rfds, wfds;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000281 std::list<Socket*> sockets;
282 std::list<Socket*>::iterator i;
283
284 // Process any incoming X events
285 TXWindow::handleXEvents(dpy);
286
287 FD_ZERO(&rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200288 FD_ZERO(&wfds);
289
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000290 FD_SET(ConnectionNumber(dpy), &rfds);
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200291 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000292 i != listeners.end();
293 i++)
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200294 FD_SET((*i)->getFd(), &rfds);
Tim Waugh892d10a2015-03-11 13:12:07 +0000295
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000296 server.getSockets(&sockets);
297 int clients_connected = 0;
298 for (i = sockets.begin(); i != sockets.end(); i++) {
299 if ((*i)->isShutdown()) {
300 server.removeSocket(*i);
301 delete (*i);
302 } else {
303 FD_SET((*i)->getFd(), &rfds);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200304 if ((*i)->outStream().bufferUsage() > 0)
305 FD_SET((*i)->getFd(), &wfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000306 clients_connected++;
307 }
308 }
309
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000310 if (!clients_connected)
311 sched.reset();
312
Pierre Ossman278e4202016-04-29 14:28:54 +0200313 wait_ms = 0;
314
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000315 if (sched.isRunning()) {
Pierre Ossman278e4202016-04-29 14:28:54 +0200316 wait_ms = sched.millisRemaining();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000317 if (wait_ms > 500) {
318 wait_ms = 500;
319 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000320 }
Pierre Ossman278e4202016-04-29 14:28:54 +0200321
Pierre Ossmana4308c92018-10-26 15:54:56 +0200322 soonestTimeout(&wait_ms, Timer::checkTimeouts());
Pierre Ossman278e4202016-04-29 14:28:54 +0200323
324 tv.tv_sec = wait_ms / 1000;
325 tv.tv_usec = (wait_ms % 1000) * 1000;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000326
327 // Do the wait...
328 sched.sleepStarted();
Pierre Ossman16419cc2016-04-29 14:29:43 +0200329 int n = select(FD_SETSIZE, &rfds, &wfds, 0,
Pierre Ossman278e4202016-04-29 14:28:54 +0200330 wait_ms ? &tv : NULL);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000331 sched.sleepFinished();
332
333 if (n < 0) {
334 if (errno == EINTR) {
335 vlog.debug("Interrupted select() system call");
336 continue;
337 } else {
338 throw rdr::SystemException("select", errno);
339 }
340 }
341
342 // Accept new VNC connections
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200343 for (std::list<SocketListener*>::iterator i = listeners.begin();
Tim Waugh892d10a2015-03-11 13:12:07 +0000344 i != listeners.end();
345 i++) {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200346 if (FD_ISSET((*i)->getFd(), &rfds)) {
347 Socket* sock = (*i)->accept();
Tim Waugh892d10a2015-03-11 13:12:07 +0000348 if (sock) {
Pierre Ossman16419cc2016-04-29 14:29:43 +0200349 sock->outStream().setBlocking(false);
Tim Waugh892d10a2015-03-11 13:12:07 +0000350 server.addSocket(sock);
351 } else {
352 vlog.status("Client connection rejected");
353 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000354 }
355 }
356
Pierre Ossmana4308c92018-10-26 15:54:56 +0200357 Timer::checkTimeouts();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000358
359 // Client list could have been changed.
360 server.getSockets(&sockets);
361
362 // Nothing more to do if there are no client connections.
363 if (sockets.empty())
364 continue;
365
366 // Process events on existing VNC connections
367 for (i = sockets.begin(); i != sockets.end(); i++) {
368 if (FD_ISSET((*i)->getFd(), &rfds))
Pierre Ossmand408ca52016-04-29 14:26:05 +0200369 server.processSocketReadEvent(*i);
Pierre Ossman16419cc2016-04-29 14:29:43 +0200370 if (FD_ISSET((*i)->getFd(), &wfds))
371 server.processSocketWriteEvent(*i);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000372 }
373
374 if (desktop.isRunning() && sched.goodTimeToPoll()) {
375 sched.newPass();
376 desktop.poll();
377 }
378 }
379
380 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000381 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000382 return 1;
383 }
384
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000385 TXWindow::handleXEvents(dpy);
386
Peter Åstrand (astrand)454d4442018-09-25 13:51:55 +0200387 // Run listener destructors; remove UNIX sockets etc
388 for (std::list<SocketListener*>::iterator i = listeners.begin();
389 i != listeners.end();
390 i++) {
391 delete *i;
392 }
393
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000394 vlog.info("Terminated");
395 return 0;
396}