You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

x0vncserver.cxx 12KB

Rebrand the source as TigerVNC. It is my hope that this patch is minimal but still complete. The rebranding was done using a script: find trunk -name .svn -prune -o -type f -exec rep.sh \{\} \; pushd trunk svn revert doc/TODO doc/registered-codes.txt doc/ft-protocol-problems.txt doc/rfbtight.tex perl -pi -e 's|tightvnc|tigervnc|g' unix/configure.ac win/configure.ac unix/README With rep.sh looking like: perl -pi -e 's|TightVNC|TigerVNC|g' "$@" perl -pi -e 's|www\.tightvnc\.com/bugs\.html|www\.tigervnc\.org|g' "$@" perl -pi -e 's|www\.tightvnc\.com|www\.tigervnc\.org|g' "$@" perl -pi -e 's|devteam\@tightvnc\.com|tigervnc-devel\@lists\.sourceforge\.net|g' "$@" perl -pi -e 's|TigerVNC Team|TightVNC Team|g' "$@" perl -pi -e 's|TigerVNC Group|TightVNC Group|g' "$@" perl -pi -e 's|TigerVNC protocol|TightVNC protocol|g' "$@" perl -pi -e 's|TigerVNC-specific|TightVNC-specific|g' "$@" perl -pi -e 's|Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TigerVNC|Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC|g' "$@" perl -pi -e 's|TigerVNC vendor|TightVNC vendor|g' "$@" perl -pi -e 's|TigerVNC extension|TightVNC extension|g' "$@" perl -pi -e 's|protocolTigerVNC|protocolTightVNC|g' "$@" perl -pi -e 's|TigerVNC additions were|TightVNC additions were|g' "$@" perl -pi -e 's|TigerVNC 1\.2|TightVNC 1\.2|g' "$@" perl -pi -e 's|TigerVNC authentication type|TightVNC authentication type|g' "$@" git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3621 3789f03b-4d11-0410-bbf8-ca57d06f2519
15 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
  3. *
  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. // FIXME: Check cases when screen width/height is not a multiply of 32.
  20. // e.g. 800x600.
  21. #include <strings.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <unistd.h>
  25. #include <errno.h>
  26. #include <rfb/Logger_stdio.h>
  27. #include <rfb/LogWriter.h>
  28. #include <rfb/VNCServerST.h>
  29. #include <rfb/Configuration.h>
  30. #include <rfb/Timer.h>
  31. #include <network/TcpSocket.h>
  32. #include <network/UnixSocket.h>
  33. #include <vncconfig/QueryConnectDialog.h>
  34. #include <signal.h>
  35. #include <X11/X.h>
  36. #include <X11/Xlib.h>
  37. #include <X11/Xutil.h>
  38. #include <x0vncserver/XDesktop.h>
  39. #include <x0vncserver/Geometry.h>
  40. #include <x0vncserver/Image.h>
  41. #include <x0vncserver/PollingScheduler.h>
  42. extern char buildtime[];
  43. using namespace rfb;
  44. using namespace network;
  45. static LogWriter vlog("Main");
  46. IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
  47. "cycle; actual interval may be dynamically "
  48. "adjusted to satisfy MaxProcessorUsage setting", 30);
  49. IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
  50. "CPU time to be consumed", 35);
  51. StringParameter displayname("display", "The X display", "");
  52. IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
  53. StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
  54. IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);
  55. IntParameter queryConnectTimeout("QueryConnectTimeout",
  56. "Number of seconds to show the Accept Connection dialog before "
  57. "rejecting the connection",
  58. 10);
  59. StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
  60. //
  61. // Allow the main loop terminate itself gracefully on receiving a signal.
  62. //
  63. static bool caughtSignal = false;
  64. static void CleanupSignalHandler(int sig)
  65. {
  66. caughtSignal = true;
  67. }
  68. class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
  69. public QueryResultCallback {
  70. public:
  71. QueryConnHandler(Display* dpy, VNCServerST* vs)
  72. : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
  73. ~QueryConnHandler() { delete queryConnectDialog; }
  74. // -=- VNCServerST::QueryConnectionHandler interface
  75. virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
  76. const char* userName,
  77. char** reason) {
  78. if (queryConnectSock) {
  79. *reason = strDup("Another connection is currently being queried.");
  80. return VNCServerST::REJECT;
  81. }
  82. if (!userName) userName = "(anonymous)";
  83. queryConnectSock = sock;
  84. CharArray address(sock->getPeerAddress());
  85. delete queryConnectDialog;
  86. queryConnectDialog = new QueryConnectDialog(display, address.buf,
  87. userName, queryConnectTimeout,
  88. this);
  89. queryConnectDialog->map();
  90. return VNCServerST::PENDING;
  91. }
  92. // -=- QueryResultCallback interface
  93. virtual void queryApproved() {
  94. server->approveConnection(queryConnectSock, true, 0);
  95. queryConnectSock = 0;
  96. }
  97. virtual void queryRejected() {
  98. server->approveConnection(queryConnectSock, false,
  99. "Connection rejected by local user");
  100. queryConnectSock = 0;
  101. }
  102. private:
  103. Display* display;
  104. VNCServerST* server;
  105. QueryConnectDialog* queryConnectDialog;
  106. network::Socket* queryConnectSock;
  107. };
  108. class FileTcpFilter : public TcpFilter
  109. {
  110. public:
  111. FileTcpFilter(const char *fname)
  112. : TcpFilter("-"), fileName(NULL), lastModTime(0)
  113. {
  114. if (fname != NULL)
  115. fileName = strdup((char *)fname);
  116. }
  117. virtual ~FileTcpFilter()
  118. {
  119. if (fileName != NULL)
  120. free(fileName);
  121. }
  122. virtual bool verifyConnection(Socket* s)
  123. {
  124. if (!reloadRules()) {
  125. vlog.error("Could not read IP filtering rules: rejecting all clients");
  126. filter.clear();
  127. filter.push_back(parsePattern("-"));
  128. return false;
  129. }
  130. return TcpFilter::verifyConnection(s);
  131. }
  132. protected:
  133. bool reloadRules()
  134. {
  135. if (fileName == NULL)
  136. return true;
  137. struct stat st;
  138. if (stat(fileName, &st) != 0)
  139. return false;
  140. if (st.st_mtime != lastModTime) {
  141. // Actually reload only if the file was modified
  142. FILE *fp = fopen(fileName, "r");
  143. if (fp == NULL)
  144. return false;
  145. // Remove all the rules from the parent class
  146. filter.clear();
  147. // Parse the file contents adding rules to the parent class
  148. char buf[32];
  149. while (readLine(buf, 32, fp)) {
  150. if (buf[0] && strchr("+-?", buf[0])) {
  151. filter.push_back(parsePattern(buf));
  152. }
  153. }
  154. fclose(fp);
  155. lastModTime = st.st_mtime;
  156. }
  157. return true;
  158. }
  159. protected:
  160. char *fileName;
  161. time_t lastModTime;
  162. private:
  163. //
  164. // NOTE: we silently truncate long lines in this function.
  165. //
  166. bool readLine(char *buf, int bufSize, FILE *fp)
  167. {
  168. if (fp == NULL || buf == NULL || bufSize == 0)
  169. return false;
  170. if (fgets(buf, bufSize, fp) == NULL)
  171. return false;
  172. char *ptr = strchr(buf, '\n');
  173. if (ptr != NULL) {
  174. *ptr = '\0'; // remove newline at the end
  175. } else {
  176. if (!feof(fp)) {
  177. int c;
  178. do { // skip the rest of a long line
  179. c = getc(fp);
  180. } while (c != '\n' && c != EOF);
  181. }
  182. }
  183. return true;
  184. }
  185. };
  186. char* programName;
  187. static void printVersion(FILE *fp)
  188. {
  189. fprintf(fp, "TigerVNC Server version %s, built %s\n",
  190. PACKAGE_VERSION, buildtime);
  191. }
  192. static void usage()
  193. {
  194. printVersion(stderr);
  195. fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
  196. fprintf(stderr, " %s --version\n", programName);
  197. fprintf(stderr,"\n"
  198. "Parameters can be turned on with -<param> or off with -<param>=0\n"
  199. "Parameters which take a value can be specified as "
  200. "-<param> <value>\n"
  201. "Other valid forms are <param>=<value> -<param>=<value> "
  202. "--<param>=<value>\n"
  203. "Parameter names are case-insensitive. The parameters are:\n\n");
  204. Configuration::listParams(79, 14);
  205. exit(1);
  206. }
  207. int main(int argc, char** argv)
  208. {
  209. initStdIOLoggers();
  210. LogWriter::setLogParams("*:stderr:30");
  211. programName = argv[0];
  212. Display* dpy;
  213. Configuration::enableServerParams();
  214. // Disable configuration parameters which we do not support
  215. Configuration::removeParam("AcceptSetDesktopSize");
  216. for (int i = 1; i < argc; i++) {
  217. if (Configuration::setParam(argv[i]))
  218. continue;
  219. if (argv[i][0] == '-') {
  220. if (i+1 < argc) {
  221. if (Configuration::setParam(&argv[i][1], argv[i+1])) {
  222. i++;
  223. continue;
  224. }
  225. }
  226. if (strcmp(argv[i], "-v") == 0 ||
  227. strcmp(argv[i], "-version") == 0 ||
  228. strcmp(argv[i], "--version") == 0) {
  229. printVersion(stdout);
  230. return 0;
  231. }
  232. usage();
  233. }
  234. usage();
  235. }
  236. CharArray dpyStr(displayname.getData());
  237. if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
  238. // FIXME: Why not vlog.error(...)?
  239. fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
  240. programName, XDisplayName(dpyStr.buf));
  241. exit(1);
  242. }
  243. signal(SIGHUP, CleanupSignalHandler);
  244. signal(SIGINT, CleanupSignalHandler);
  245. signal(SIGTERM, CleanupSignalHandler);
  246. std::list<SocketListener*> listeners;
  247. try {
  248. TXWindow::init(dpy,"x0vncserver");
  249. Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
  250. DisplayHeight(dpy, DefaultScreen(dpy)));
  251. if (geo.getRect().is_empty()) {
  252. vlog.error("Exiting with error");
  253. return 1;
  254. }
  255. XDesktop desktop(dpy, &geo);
  256. VNCServerST server("x0vncserver", &desktop);
  257. QueryConnHandler qcHandler(dpy, &server);
  258. server.setQueryConnectionHandler(&qcHandler);
  259. if (rfbunixpath.getValueStr()[0] != '\0') {
  260. listeners.push_back(new network::UnixListener(rfbunixpath, rfbunixmode));
  261. vlog.info("Listening on %s (mode %04o)", (const char*)rfbunixpath, (int)rfbunixmode);
  262. } else {
  263. createTcpListeners(&listeners, 0, (int)rfbport);
  264. vlog.info("Listening on port %d", (int)rfbport);
  265. }
  266. const char *hostsData = hostsFile.getData();
  267. FileTcpFilter fileTcpFilter(hostsData);
  268. if (strlen(hostsData) != 0)
  269. for (std::list<SocketListener*>::iterator i = listeners.begin();
  270. i != listeners.end();
  271. i++)
  272. (*i)->setFilter(&fileTcpFilter);
  273. delete[] hostsData;
  274. PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
  275. while (!caughtSignal) {
  276. int wait_ms;
  277. struct timeval tv;
  278. fd_set rfds, wfds;
  279. std::list<Socket*> sockets;
  280. std::list<Socket*>::iterator i;
  281. // Process any incoming X events
  282. TXWindow::handleXEvents(dpy);
  283. FD_ZERO(&rfds);
  284. FD_ZERO(&wfds);
  285. FD_SET(ConnectionNumber(dpy), &rfds);
  286. for (std::list<SocketListener*>::iterator i = listeners.begin();
  287. i != listeners.end();
  288. i++)
  289. FD_SET((*i)->getFd(), &rfds);
  290. server.getSockets(&sockets);
  291. int clients_connected = 0;
  292. for (i = sockets.begin(); i != sockets.end(); i++) {
  293. if ((*i)->isShutdown()) {
  294. server.removeSocket(*i);
  295. delete (*i);
  296. } else {
  297. FD_SET((*i)->getFd(), &rfds);
  298. if ((*i)->outStream().bufferUsage() > 0)
  299. FD_SET((*i)->getFd(), &wfds);
  300. clients_connected++;
  301. }
  302. }
  303. if (!clients_connected)
  304. sched.reset();
  305. wait_ms = 0;
  306. if (sched.isRunning()) {
  307. wait_ms = sched.millisRemaining();
  308. if (wait_ms > 500) {
  309. wait_ms = 500;
  310. }
  311. }
  312. soonestTimeout(&wait_ms, server.checkTimeouts());
  313. tv.tv_sec = wait_ms / 1000;
  314. tv.tv_usec = (wait_ms % 1000) * 1000;
  315. // Do the wait...
  316. sched.sleepStarted();
  317. int n = select(FD_SETSIZE, &rfds, &wfds, 0,
  318. wait_ms ? &tv : NULL);
  319. sched.sleepFinished();
  320. if (n < 0) {
  321. if (errno == EINTR) {
  322. vlog.debug("Interrupted select() system call");
  323. continue;
  324. } else {
  325. throw rdr::SystemException("select", errno);
  326. }
  327. }
  328. // Accept new VNC connections
  329. for (std::list<SocketListener*>::iterator i = listeners.begin();
  330. i != listeners.end();
  331. i++) {
  332. if (FD_ISSET((*i)->getFd(), &rfds)) {
  333. Socket* sock = (*i)->accept();
  334. if (sock) {
  335. sock->outStream().setBlocking(false);
  336. server.addSocket(sock);
  337. } else {
  338. vlog.status("Client connection rejected");
  339. }
  340. }
  341. }
  342. server.checkTimeouts();
  343. // Client list could have been changed.
  344. server.getSockets(&sockets);
  345. // Nothing more to do if there are no client connections.
  346. if (sockets.empty())
  347. continue;
  348. // Process events on existing VNC connections
  349. for (i = sockets.begin(); i != sockets.end(); i++) {
  350. if (FD_ISSET((*i)->getFd(), &rfds))
  351. server.processSocketReadEvent(*i);
  352. if (FD_ISSET((*i)->getFd(), &wfds))
  353. server.processSocketWriteEvent(*i);
  354. }
  355. if (desktop.isRunning() && sched.goodTimeToPoll()) {
  356. sched.newPass();
  357. desktop.poll();
  358. }
  359. }
  360. } catch (rdr::Exception &e) {
  361. vlog.error("%s", e.str());
  362. return 1;
  363. }
  364. TXWindow::handleXEvents(dpy);
  365. vlog.info("Terminated");
  366. return 0;
  367. }