Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- util.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "cleanup.h"
     37 #include "win_utils.h"
     38 #include "unixpw.h"
     39 #include "connections.h"
     40 
     41 struct timeval _mysleep;
     42 
     43 /* this is only for debugging mutexes. see util.h */
     44 int hxl = 0;
     45 
     46 #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
     47 MUTEX(x11Mutex);
     48 MUTEX(scrollMutex);
     49 #endif
     50 
     51 int nfix(int i, int n);
     52 int nmin(int n, int m);
     53 int nmax(int n, int m);
     54 int nabs(int n);
     55 double dabs(double x);
     56 void lowercase(char *str);
     57 void uppercase(char *str);
     58 char *lblanks(char *str);
     59 void strzero(char *str);
     60 int scan_hexdec(char *str, unsigned long *num);
     61 int parse_geom(char *str, int *wp, int *hp, int *xp, int *yp, int W, int H);
     62 void set_env(char *name, char *value);
     63 char *bitprint(unsigned int st, int nbits);
     64 char *get_user_name(void);
     65 char *get_home_dir(void);
     66 char *get_shell(void);
     67 char *this_host(void);
     68 
     69 int match_str_list(char *str, char **list);
     70 char **create_str_list(char *cslist);
     71 
     72 double dtime(double *);
     73 double dtime0(double *);
     74 double dnow(void);
     75 double dnowx(void);
     76 double rnow(void);
     77 double rfac(void);
     78 
     79 int rfbPE(long usec);
     80 void rfbCFD(long usec);
     81 
     82 double rect_overlap(int x1, int y1, int x2, int y2, int X1, int Y1,
     83     int X2, int Y2);
     84 
     85 char *choose_title(char *display);
     86 
     87 
     88 /*
     89  * routine to keep 0 <= i < n
     90  */
     91 int nfix(int i, int n) {
     92 	if (i < 0) {
     93 		i = 0;
     94 	} else if (i >= n) {
     95 		i = n - 1;
     96 	}
     97 	return i;
     98 }
     99 
    100 int nmin(int n, int m) {
    101 	if (n < m) {
    102 		return n;
    103 	} else {
    104 		return m;
    105 	}
    106 }
    107 
    108 int nmax(int n, int m) {
    109 	if (n > m) {
    110 		return n;
    111 	} else {
    112 		return m;
    113 	}
    114 }
    115 
    116 int nabs(int n) {
    117 	if (n < 0) {
    118 		return -n;
    119 	} else {
    120 		return n;
    121 	}
    122 }
    123 
    124 double dabs(double x) {
    125 	if (x < 0.0) {
    126 		return -x;
    127 	} else {
    128 		return x;
    129 	}
    130 }
    131 
    132 void lowercase(char *str) {
    133 	char *p;
    134 	if (str == NULL) {
    135 		return;
    136 	}
    137 	p = str;
    138 	while (*p != '\0') {
    139 		*p = tolower((unsigned char) (*p));
    140 		p++;
    141 	}
    142 }
    143 
    144 void uppercase(char *str) {
    145 	char *p;
    146 	if (str == NULL) {
    147 		return;
    148 	}
    149 	p = str;
    150 	while (*p != '\0') {
    151 		*p = toupper((unsigned char) (*p));
    152 		p++;
    153 	}
    154 }
    155 
    156 char *lblanks(char *str) {
    157 	char *p = str;
    158 	while (*p != '\0') {
    159 		if (! isspace((unsigned char) (*p))) {
    160 			break;
    161 		}
    162 		p++;
    163 	}
    164 	return p;
    165 }
    166 
    167 void strzero(char *str) {
    168 	char *p = str;
    169 	if (p != NULL) {
    170 		while (*p != '\0') {
    171 			*p = '\0';
    172 			p++;
    173 		}
    174 	}
    175 }
    176 
    177 int is_decimal(char *str) {
    178 	char *p = str;
    179 	if (p != NULL) {
    180 		int first = 1;
    181 		while (*p != '\0') {
    182 			if (first && *p == '-') {
    183 				;
    184 			} else if (isdigit((int) *p)) {
    185 				;
    186 			} else {
    187 				return 0;
    188 			}
    189 			first = 0;
    190 			p++;
    191 		}
    192 		return 1;
    193 	}
    194 	return 0;
    195 }
    196 
    197 int scan_hexdec(char *str, unsigned long *num) {
    198 	if (sscanf(str, "0x%lx", num) != 1) {
    199 		if (sscanf(str, "%lu", num) != 1) {
    200 			return 0;
    201 		}
    202 	}
    203 	return 1;
    204 }
    205 
    206 int parse_geom(char *str, int *wp, int *hp, int *xp, int *yp, int W, int H) {
    207 	int w, h, x, y;
    208 	if (! str) {
    209 		return 0;
    210 	}
    211 	/* handle +/-x and +/-y */
    212 	if (sscanf(str, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) {
    213 		;
    214 	} else if (sscanf(str, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) {
    215 		w = nabs(w);
    216 		x = W - x - w;
    217 	} else if (sscanf(str, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) {
    218 		h = nabs(h);
    219 		y = H - y - h;
    220 	} else if (sscanf(str, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) {
    221 		w = nabs(w);
    222 		h = nabs(h);
    223 		x = W - x - w;
    224 		y = H - y - h;
    225 	} else {
    226 		return 0;
    227 	}
    228 	*wp = w;
    229 	*hp = h;
    230 	*xp = x;
    231 	*yp = y;
    232 	return 1;
    233 }
    234 
    235 void set_env(char *name, char *value) {
    236 	char *str;
    237 	if (! name) {
    238 		return;
    239 	}
    240 	if (! value) {
    241 		value = "";
    242 	}
    243 	str = (char *) malloc(strlen(name) + 1 + strlen(value) + 1);
    244 	sprintf(str, "%s=%s", name, value);
    245 	putenv(str);
    246 }
    247 
    248 char *bitprint(unsigned int st, int nbits) {
    249 	static char str[33];
    250 	int i, mask;
    251 	if (nbits > 32) {
    252 		nbits = 32;
    253 	}
    254 	for (i=0; i<nbits; i++) {
    255 		str[i] = '0';
    256 	}
    257 	str[nbits] = '\0';
    258 	mask = 1;
    259 	for (i=nbits-1; i>=0; i--) {
    260 		if (st & mask) {
    261 			str[i] = '1';
    262 		}
    263 		mask = mask << 1;
    264 	}
    265 	return str;	/* take care to use or copy immediately */
    266 }
    267 
    268 char *get_user_name(void) {
    269 	char *user = NULL;
    270 
    271 	user = getenv("USER");
    272 	if (user == NULL) {
    273 		user = getenv("LOGNAME");
    274 	}
    275 
    276 #if LIBVNCSERVER_HAVE_PWD_H
    277 	if (user == NULL) {
    278 		struct passwd *pw = getpwuid(getuid());
    279 		if (pw) {
    280 			user = pw->pw_name;
    281 		}
    282 	}
    283 #endif
    284 
    285 	if (user) {
    286 		return(strdup(user));
    287 	} else {
    288 		return(strdup("unknown-user"));
    289 	}
    290 }
    291 
    292 char *get_home_dir(void) {
    293 	char *home = NULL;
    294 
    295 	home = getenv("HOME");
    296 
    297 #if LIBVNCSERVER_HAVE_PWD_H
    298 	if (home == NULL) {
    299 		struct passwd *pw = getpwuid(getuid());
    300 		if (pw) {
    301 			home = pw->pw_dir;
    302 		}
    303 	}
    304 #endif
    305 
    306 	if (home) {
    307 		return(strdup(home));
    308 	} else {
    309 		return(strdup("/"));
    310 	}
    311 }
    312 
    313 char *get_shell(void) {
    314 	char *shell = NULL;
    315 
    316 	shell = getenv("SHELL");
    317 
    318 #if LIBVNCSERVER_HAVE_PWD_H
    319 	if (shell == NULL) {
    320 		struct passwd *pw = getpwuid(getuid());
    321 		if (pw) {
    322 			shell = pw->pw_shell;
    323 		}
    324 	}
    325 #endif
    326 
    327 	if (shell) {
    328 		return(strdup(shell));
    329 	} else {
    330 		return(strdup("/bin/sh"));
    331 	}
    332 }
    333 
    334 /*
    335  * utility to get the current host name
    336  */
    337 char *this_host(void) {
    338 	char host[MAXN];
    339 #if LIBVNCSERVER_HAVE_GETHOSTNAME
    340 	if (gethostname(host, MAXN) == 0) {
    341 		host[MAXN-1] = '\0';
    342 		return strdup(host);
    343 	} else if (UT.nodename) {
    344 		return strdup(UT.nodename);
    345 	}
    346 #endif
    347 	return NULL;
    348 }
    349 
    350 int match_str_list(char *str, char **list) {
    351 	int i = 0, matched = 0;
    352 
    353 	if (! str || ! list) {
    354 		return 0;
    355 	}
    356 	while (list[i] != NULL) {
    357 		if (!strcmp(list[i], "*")) {
    358 			matched = 1;
    359 			break;
    360 		} else if (strstr(str, list[i])) {
    361 			matched = 1;
    362 			break;
    363 		}
    364 		i++;
    365 	}
    366 	return matched;
    367 }
    368 
    369 char **create_str_list(char *cslist) {
    370 	int i, n;
    371 	char *p, *str;
    372 	char **list = NULL;
    373 
    374 	if (! cslist) {
    375 		return NULL;
    376 	}
    377 
    378 	str = strdup(cslist);
    379 	n = 1;
    380 	p = str;
    381 	while (*p != '\0') {
    382 		if (*p == ',') {
    383 			n++;
    384 		}
    385 		p++;
    386 	}
    387 
    388 	/* the extra last one holds NULL */
    389 	list = (char **) calloc((n+1)*sizeof(char *), 1);
    390 
    391 	p = strtok(str, ",");
    392 	i = 0;
    393 	while (p && i < n) {
    394 		list[i++] = strdup(p);
    395 		p = strtok(NULL, ",");
    396 	}
    397 	free(str);
    398 
    399 	return list;
    400 }
    401 
    402 /*
    403  * simple function for measuring sub-second time differences, using
    404  * a double to hold the value.
    405  */
    406 double dtime(double *t_old) {
    407 	/*
    408 	 * usage: call with 0.0 to initialize, subsequent calls give
    409 	 * the time difference since last call.
    410 	 */
    411 	double t_now, dt;
    412 	struct timeval now;
    413 
    414 	gettimeofday(&now, NULL);
    415 	t_now = now.tv_sec + ( (double) now.tv_usec/1000000. );
    416 	if (*t_old == 0.0) {
    417 		*t_old = t_now;
    418 		return t_now;
    419 	}
    420 	dt = t_now - *t_old;
    421 	*t_old = t_now;
    422 	return(dt);
    423 }
    424 
    425 /* common dtime() activities: */
    426 double dtime0(double *t_old) {
    427 	*t_old = 0.0;
    428 	return dtime(t_old);
    429 }
    430 
    431 double dnow(void) {
    432 	double t;
    433 	return dtime0(&t);
    434 }
    435 
    436 double dnowx(void) {
    437 	return dnow() - x11vnc_start;
    438 }
    439 
    440 double rnow(void) {
    441 	double t = dnow();
    442 	t = t - ((int) t);
    443 	if (t > 1.0) {
    444 		t = 1.0;
    445 	} else if (t < 0.0) {
    446 		t = 0.0;
    447 	}
    448 	return t;
    449 }
    450 
    451 double rfac(void) {
    452 	double f;
    453 	static int first = 1;
    454 
    455 	if (first) {
    456 		unsigned int s;
    457 		if (getenv("RAND_SEED")) {
    458 			s = (unsigned int) atoi(getenv("RAND_SEED"));
    459 		} else {
    460 			s = (unsigned int) ((int) getpid() + 100000 * rnow());
    461 		}
    462 		srand(s);
    463 		first = 0;
    464 	}
    465 
    466 	f = (double) rand();
    467 	f = f / ((double) RAND_MAX);
    468 
    469 	return f;
    470 }
    471 
    472 void check_allinput_rate(void) {
    473 	static double last_all_input_check = 0.0;
    474 	static int set = 0, verb = -1;
    475 
    476 	if (use_threads) {
    477 		return;
    478 	}
    479 	if (verb < 0) {
    480 		verb = 0;
    481 		if (getenv("RATE_VERB")) verb = 1;
    482 	}
    483 	if (! set) {
    484 		set = 1;
    485 		last_all_input_check = dnow();
    486 	} else {
    487 		int dt = 5;
    488 		if (x11vnc_current > last_all_input_check + dt) {
    489 			int n, nq = 0;
    490 			while ((n = rfbCheckFds(screen, 0))) {
    491 				nq += n;
    492 			}
    493 			if (verb) fprintf(stderr, "nqueued: %d\n", nq);
    494 			if (getenv("CHECK_RATE") && nq > 18 * dt) {
    495 				double rate = nq / dt;
    496 				if (verb) rfbLog("check_allinput_rate:\n");
    497 				if (verb) rfbLog("Client is sending %.1f extra requests per second for the\n", rate);
    498 				if (verb) rfbLog("past %d seconds! (queued: %d)\n", dt, nq);
    499 				if (strstr(getenv("CHECK_RATE"), "allinput") && !all_input && !handle_events_eagerly) {
    500 					rfbLog("Switching to -allpinput mode.\n");
    501 					all_input = 1;
    502 				}
    503 			}
    504 			set = 0;
    505 		}
    506 	}
    507 }
    508 
    509 static void do_allinput(long usec) {
    510 	static double last = 0.0;
    511 	static int meas = 0, verb = -1;
    512 	int n, f = 1, cnt = 0, m = 0;
    513 	long usec0;
    514 	double now;
    515 	if (!screen || !screen->clientHead) {
    516 		return;
    517 	}
    518 	if (use_threads) {
    519 		return;
    520 	}
    521 	if (usec < 0) {
    522 		usec = 0;
    523 	}
    524 	usec0 = usec;
    525 	if (last == 0.0) {
    526 		last = dnow();
    527 	}
    528 	if (verb < 0) {
    529 		verb = 0;
    530 		if (getenv("RATE_VERB")) verb = 1;
    531 	}
    532 	while ((n = rfbCheckFds(screen, usec)) > 0) {
    533 		if (f) {
    534 			if (verb) fprintf(stderr, " *");
    535 			f = 0;
    536 		}
    537 		if (cnt++ > 30) {
    538 			break;
    539 		}
    540 		meas += n;
    541 		m += n;
    542 	}
    543 	if (verb) fprintf(stderr, "+%d/%d", cnt, m);
    544 	now = dnow();
    545 	if (now > last + 2.0) {
    546 		double rate = meas / (now - last);
    547 		if (verb) fprintf(stderr, "\n allinput rate: %.2f ", rate);
    548 		meas = 0;
    549 		last = dnow();
    550 	}
    551 }
    552 
    553 /*
    554  * utility wrapper to call rfbProcessEvents
    555  * checks that we are not in threaded mode.
    556  */
    557 #define USEC_MAX 999999		/* libvncsever assumes < 1 second */
    558 int rfbPE(long usec) {
    559 	int uip0 = unixpw_in_progress;
    560 	static int check_rate = -1;
    561 	int res = 0;
    562 	if (! screen) {
    563 		return res;
    564 	}
    565  	if (unixpw && unixpw_in_progress && !unixpw_in_rfbPE) {
    566 		rfbLog("unixpw_in_rfbPE: skipping rfbPE\n");
    567  		return res;
    568  	}
    569 
    570 	if (debug_tiles > 2) {
    571 		double tm = dnow();
    572 		fprintf(stderr, "rfbPE(%d)  t: %.4f\n",
    573 		    (int) usec, tm - x11vnc_start);
    574 	}
    575 
    576 	if (usec > USEC_MAX) {
    577 		usec = USEC_MAX;
    578 	}
    579 	if (! use_threads) {
    580 		rfbBool r;
    581 		r = rfbProcessEvents(screen, usec);
    582 		if (r) {
    583 			res = 1;
    584 		}
    585 	}
    586 
    587  	if (unixpw && unixpw_in_progress && !uip0) {
    588 		if (!unixpw_in_rfbPE) {
    589 			rfbLog("rfbPE: got new client in non-rfbPE\n");
    590 			;	/* this is new unixpw client  */
    591 		}
    592  	}
    593 
    594 	if (ipv6_listen) {
    595 		check_ipv6_listen(usec);
    596 	}
    597 	if (unix_sock) {
    598 		check_unix_sock(usec);
    599 	}
    600 	if (check_rate != 0) {
    601 		if (check_rate < 0) {
    602 			if (getenv("CHECK_RATE")) {
    603 				check_rate = 1;
    604 			} else {
    605 				check_rate = 0;
    606 			}
    607 		}
    608 		if (check_rate && !all_input && x11vnc_current < last_client + 45)  {
    609 			check_allinput_rate();
    610 		}
    611 	}
    612 	if (all_input) {
    613 		do_allinput(usec);
    614 	}
    615 	return res;
    616 }
    617 
    618 void rfbCFD(long usec) {
    619 	int uip0 = unixpw_in_progress;
    620 	if (! screen) {
    621 		return;
    622 	}
    623  	if (unixpw && unixpw_in_progress && !unixpw_in_rfbPE) {
    624 		static int msgs = 0;
    625 		static double last_reset = 0.0;
    626 		if (dnow() > last_reset + 5.0) {
    627 			msgs = 0;
    628 			last_reset = dnow();
    629 		}
    630 		if (msgs++ < 10) {
    631 			rfbLog("unixpw_in_rfbPE: skipping rfbCFD\n");
    632 			if (msgs == 10) {
    633 				rfbLog("unixpw_in_rfbPE: skipping rfbCFD ...\n");
    634 			}
    635 		}
    636  		return;
    637  	}
    638 	if (usec > USEC_MAX) {
    639 		usec = USEC_MAX;
    640 	}
    641 
    642 	if (debug_tiles > 2) {
    643 		double tm = dnow();
    644 		fprintf(stderr, "rfbCFD(%d) t: %.4f\n",
    645 		    (int) usec, tm - x11vnc_start);
    646 	}
    647 
    648 
    649 	if (! use_threads) {
    650 		if (all_input) {
    651 			do_allinput(usec);
    652 		} else {
    653 			if (handle_events_eagerly) {
    654 				screen->handleEventsEagerly = TRUE;
    655 			} else {
    656 				screen->handleEventsEagerly = FALSE;
    657 			}
    658 			rfbCheckFds(screen, usec);
    659 		}
    660 	}
    661 
    662  	if (unixpw && unixpw_in_progress && !uip0) {
    663 		if (!unixpw_in_rfbPE) {
    664 			rfbLog("rfbCFD: got new client in non-rfbPE\n");
    665 			;	/* this is new unixpw client  */
    666 		}
    667  	}
    668 }
    669 
    670 double rect_overlap(int x1, int y1, int x2, int y2, int X1, int Y1,
    671     int X2, int Y2) {
    672 	double a, A, o;
    673 	sraRegionPtr r, R;
    674 	sraRectangleIterator *iter;
    675 	sraRect rt;
    676 
    677 	a = nabs((x2 - x1) * (y2 - y1));
    678 	A = nabs((X2 - X1) * (Y2 - Y1));
    679 
    680 	if (a == 0 || A == 0) {
    681 		return 0.0;
    682 	}
    683 
    684 	r = sraRgnCreateRect(x1, y1, x2, y2);
    685 	R = sraRgnCreateRect(X1, Y1, X2, Y2);
    686 
    687 	sraRgnAnd(r, R);
    688 
    689 	o = 0.0;
    690 	iter = sraRgnGetIterator(r);
    691 	while (sraRgnIteratorNext(iter, &rt)) {
    692 		o += nabs( (rt.x2 - rt.x1) * (rt.y2 - rt.y1) );
    693 	}
    694 	sraRgnReleaseIterator(iter);
    695 
    696 	sraRgnDestroy(r);
    697 	sraRgnDestroy(R);
    698 
    699 	if (a < A) {
    700 		o = o/a;
    701 	} else {
    702 		o = o/A;
    703 	}
    704 	return o;
    705 }
    706 
    707 /*
    708  * choose a desktop name
    709  */
    710 char *choose_title(char *display) {
    711 	static char title[(MAXN+10)];
    712 
    713 	memset(title, 0, sizeof(title));
    714 	strcpy(title, "x11vnc");
    715 
    716 	if (display == NULL) {
    717 		display = getenv("DISPLAY");
    718 	}
    719 
    720 #ifdef MACOSX
    721 	if (display == NULL || strstr(display, "/tmp/") == display) {
    722 		char *u = get_user_name();
    723 		char *th = this_host();
    724 		if (strlen(u) > MAXN/4)  {
    725 			u = "someone";
    726 		}
    727 		strcpy(title, u);
    728 		if (th == NULL && UT.nodename) {
    729 			th = UT.nodename;
    730 		}
    731 		if (th) {
    732 			strcat(title, "@");
    733 			strncat(title, th, MAXN - strlen(title));
    734 		}
    735 		return title;
    736 	}
    737 #endif
    738 
    739 	if (display == NULL) {
    740 		return title;
    741 	}
    742 
    743 	/* use display: */
    744 	title[0] = '\0';
    745 	if (display[0] == ':') {
    746 		char *th = this_host();
    747 		if (th != NULL) {
    748 			strncpy(title, th, MAXN - strlen(title));
    749 		}
    750 	}
    751 	strncat(title, display, MAXN - strlen(title));
    752 	X_LOCK;
    753 	if (subwin && dpy && valid_window(subwin, NULL, 0)) {
    754 #if !NO_X11
    755 		char *name = NULL;
    756 		int do_appshare = getenv("X11VNC_APPSHARE_ACTIVE") ? 1 : 0;
    757 		if (0 && do_appshare) {
    758 			title[0] = '\0';
    759 		}
    760 		if (XFetchName(dpy, subwin, &name)) {
    761 			if (name) {
    762 				if (title[0] != '\0') {
    763 					strncat(title, " ",  MAXN - strlen(title));
    764 				}
    765 				strncat(title, name, MAXN - strlen(title));
    766 				free(name);
    767 			}
    768 		}
    769 		if (do_appshare) {
    770 			Window c;
    771 			int x, y;
    772 			if (xtranslate(subwin, rootwin, 0, 0, &x, &y, &c, 1)) {
    773 				char tmp[32];
    774 				if (scaling) {
    775 					x *= scale_fac_x;
    776 					y *= scale_fac_y;
    777 				}
    778 				sprintf(tmp, " XY=%d,%d", x, y);
    779 				strncat(title, tmp, MAXN - strlen(title));
    780 			}
    781 			rfbLog("appshare title: %s\n", title);
    782 		}
    783 #endif	/* NO_X11 */
    784 	}
    785 	X_UNLOCK;
    786 	return title;
    787 }
    788