Home | History | Annotate | Download | only in misc
      1 #!/bin/sh
      2 # ----------------------------------------------------------------------
      3 #    Copyright (C) 2005-2010 Karl J. Runge <runge (at] karlrunge.com> 
      4 #    All rights reserved.
      5 # 
      6 # This file is part of Xdummy.
      7 # 
      8 # Xdummy is free software; you can redistribute it and/or modify
      9 # it under the terms of the GNU General Public License as published by
     10 # the Free Software Foundation; either version 2 of the License, or (at
     11 # your option) any later version.
     12 # 
     13 # Xdummy is distributed in the hope that it will be useful,
     14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 # GNU General Public License for more details.
     17 # 
     18 # You should have received a copy of the GNU General Public License
     19 # along with Xdummy; if not, write to the Free Software
     20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     21 # or see <http://www.gnu.org/licenses/>.
     22 # ----------------------------------------------------------------------
     23 # 
     24 # 
     25 # Xdummy: an LD_PRELOAD hack to run a stock Xorg(1) or XFree86(1) server
     26 # with the "dummy" video driver to make it avoid Linux VT switching, etc.
     27 #
     28 # Run "Xdummy -help" for more info.
     29 #
     30 install=""
     31 uninstall=""
     32 runit=1
     33 prconf=""
     34 notweak=""
     35 root=""
     36 nosudo=""
     37 xserver=""
     38 geom=""
     39 nomodelines=""
     40 depth=""
     41 debug=""
     42 strace=""
     43 cmdline_config=""
     44 
     45 PATH=$PATH:/bin:/usr/bin
     46 export PATH
     47 
     48 program=`basename "$0"`
     49 
     50 help () {
     51 	${PAGER:-more} << END
     52 $program:
     53 
     54     A hack to run a stock Xorg(1) or XFree86(1) X server with the "dummy"
     55     (RAM-only framebuffer) video driver such that it AVOIDS the Linux VT
     56     switching, opening device files in /dev, keyboard and mouse conflicts,
     57     and other problems associated with the normal use of "dummy".
     58 
     59     In other words, it tries to make Xorg/XFree86 with the "dummy"
     60     device driver act more like Xvfb(1).
     61 
     62     The primary motivation for the Xdummy script is to provide a virtual X
     63     server for x11vnc but with more features than Xvfb (or Xvnc); however
     64     it could be used for other reasons (e.g. better automated testing
     65     than with Xvfb.)  One nice thing is the dummy server supports RANDR
     66     dynamic resizing while Xvfb does not.
     67 
     68     So, for example, x11vnc+Xdummy terminal services are a little better
     69     than x11vnc+Xvfb.
     70 
     71     To achieve this, while running the real Xserver $program intercepts
     72     system and library calls via the LD_PRELOAD method and modifies
     73     the behavior to make it work correctly (e.g. avoid the VT stuff.)
     74     LD_PRELOAD tricks are usually "clever hacks" and so might not work
     75     in all situations or break when something changes.
     76 
     77     WARNING: Take care in using Xdummy, although it never has it is
     78     possible that it could damage hardware.  One can use the -prconf
     79     option to have it print out the xorg.conf config that it would use
     80     and then inspect it carefully before actually using it.
     81 
     82     This program no longer needs to be run as root as of 12/2009.
     83     However, if there are problems for certain situations (usually older
     84     servers) it may perform better if run as root (use the -root option.)
     85     When running as root remember the previous paragraph and that Xdummy
     86     comes without any warranty.
     87 
     88     gcc/cc and other build tools are required for this script to be able
     89     to compile the LD_PRELOAD shared object.  Be sure they are installed
     90     on the system.  See -install and -uninstall described below.
     91 
     92     Your Linux distribution may not install the dummy driver by default,
     93     e.g:
     94 
     95         /usr/lib/xorg/modules/drivers/dummy_drv.so
     96     
     97     some have it in a package named xserver-xorg-video-dummy you that
     98     need to install.
     99 
    100 Usage:
    101 
    102 	$program <${program}-args> <Xserver-args>
    103 
    104 	(actually, the arguments can be supplied in any order.)
    105 
    106 Examples:
    107 
    108 	$program -install
    109 
    110 	$program :1
    111 
    112 	$program -debug :1
    113 
    114 	$program -tmpdir ~/mytmp :1 -nolisten tcp
    115 
    116 startx example:
    117 
    118 	startx -e bash -- $program :2 -depth 16
    119 
    120 	(if startx needs to be run as root, you can su(1) to a normal
    121 	user in the bash shell and then launch ~/.xinitrc or ~/.xsession,
    122 	gnome-session, startkde, startxfce4, etc.)
    123 
    124 xdm example:
    125 
    126 	xdm -config /usr/local/dummy/xdm-config -nodaemon
    127 
    128 	where the xdm-config file has line:
    129 
    130 	     DisplayManager.servers:         /usr/local/dummy/Xservers
    131 
    132 	and /usr/local/dummy/Xservers has lines:
    133 
    134 	     :1 local /usr/local/dummy/Xdummy :1 -debug
    135 	     :2 local /usr/local/dummy/Xdummy :2 -debug
    136 
    137         (-debug is optional)
    138 
    139 gdm/kdm example:
    140 
    141 	TBD.
    142 
    143 Root permission and x11vnc:
    144 
    145 	Update: as of 12/2009 this program no longer must be run as root.
    146 	So try it as non-root before running it as root and/or the
    147 	following schemes.
    148 
    149 	In some circumstances X server program may need to be run as root.
    150 	If so, one could run x11vnc as root with -unixpw (it switches
    151 	to the user that logs in) and that may be OK, some other ideas:
    152 
    153 	- add this to sudo via visudo:
    154 
    155 		ALL ALL = NOPASSWD: /usr/local/bin/Xdummy
    156 
    157 	- use this little suid wrapper:
    158 /* 
    159  * xdummy.c
    160  *
    161    cc -o ./xdummy xdummy.c
    162    sudo cp ./xdummy /usr/local/bin/xdummy
    163    sudo chown root:root /usr/local/bin/xdummy
    164    sudo chmod u+s /usr/local/bin/xdummy
    165  *
    166  */
    167 #include <unistd.h>
    168 #include <stdlib.h>
    169 #include <sys/types.h>
    170 #include <stdio.h>
    171 
    172 int main (int argc, char *argv[]) {
    173 	extern char **environ;
    174 	char str[100];
    175 	sprintf(str, "XDUMMY_UID=%d", (int) getuid());
    176 	putenv(str);
    177 	setuid(0);  
    178 	setgid(0);
    179 	execv("/usr/local/bin/Xdummy", argv); 
    180 	exit(1);
    181 	return 1;
    182 }
    183 
    184 
    185 Options:
    186 
    187     ${program}-args:
    188 
    189 	-install	Compile the LD_PRELOAD shared object and install it
    190 			next to the $program script file as:
    191 
    192 			  $0.so
    193 
    194 			When that file exists it is used as the LD_PRELOAD
    195 			shared object without recompiling.  Otherwise,
    196 			each time $program is run the LD_PRELOAD shared
    197 			object is compiled as a file in /tmp (or -tmpdir)
    198 
    199 			If you set the environment variable
    200 			INTERPOSE_GETUID=1 when building, then when
    201 			$program is run as an ordinary user, the shared
    202 			object will interpose getuid() calls and pretend
    203 			to be root.  Otherwise it doesn't pretend to
    204 			be root.
    205 
    206 			You can also set the CFLAGS environment variable
    207 			to anything else you want on the compile cmdline.
    208 
    209 	-uninstall	Remove the file:
    210 
    211 			  $0.so
    212 
    213 			The LD_PRELOAD shared object will then be compiled
    214 			each time this program is run.
    215 
    216 	The X server is not started under -install, -uninstall, or -prconf.
    217 
    218 
    219 	:N		The DISPLAY (e.g. :15) is often the first
    220 			argument.  It is passed to the real X server and
    221 			also used by the Xdummy script as an identifier.
    222 
    223 	-geom geom1[,geom2...]	Take the geometry (e.g. 1024x768) or list
    224 			of geometries and insert them into the Screen
    225 			section of the tweaked X server config file.
    226 			Use this to have a different geometry than the
    227 			one(s) in the system config file.
    228 
    229 			The option -geometry can be used instead of -geom;
    230 			x11vnc calls Xdummy and Xvfb this way.
    231 
    232 	-nomodelines	When you specify -geom/-geometry, $program will
    233 			create Modelines for each geometry and put them
    234 			in the Monitor section.  If you do not want this
    235 			then supply -nomodelines.
    236 
    237 	-depth n	Use pixel color depth n (e.g. 8, 16, or 24). This
    238 			makes sure the X config file has a Screen.Display
    239 			subsection of this depth.  Note this option is
    240 			ALSO passed to the X server.
    241 
    242 	-DEPTH n	Same as -depth, except not passed to X server.
    243 
    244 	-tmpdir dir	Specify a temporary directory, owned by you and
    245 			only writable by you.  This is used in place of
    246 			/tmp/Xdummy.\$USER/..  to place the $program.so
    247 			shared object, tweaked config files, etc.
    248 
    249 	-nonroot	Run in non-root mode (working 12/2009, now default)
    250 
    251 	-root		Run as root (may still be needed in some
    252 			environments.)  Same as XDUMMY_RUN_AS_ROOT=1.
    253 
    254 	-nosudo		Do not try to use sudo(1) when re-running as root,
    255 			use su(1) instead.
    256 
    257 	-xserver path	Specify the path to the Xserver to use.  Default
    258 			is to try "Xorg" first and then "XFree86".  If
    259 			those are not in \$PATH, it tries these locations:
    260 				/usr/bin/Xorg
    261 				/usr/X11R6/bin/Xorg
    262 				/usr/X11R6/bin/XFree86
    263 
    264 	-n		Do not run the command to start the X server,
    265 			just show the command that $program would run.
    266 			The LD_PRELOAD shared object will be built,
    267 			if needed.  Also note any XDUMMY* environment
    268 			variables that need to be set.
    269 
    270 	-prconf		Print, to stdout, the tweaked Xorg/XFree86
    271 			config file (-config and -xf86config server
    272 			options, respectively.)  The Xserver is not
    273 			started.
    274 
    275 	-notweak	Do not tweak (modify) the Xorg/XFree86 config file
    276 			(system or server command line) at all.  The -geom
    277 			and similar config file modifications are ignored.
    278 
    279 			It is up to you to make sure it is a working
    280 			config file (e.g. "dummy" driver, etc.)
    281 			Perhaps you want to use a file based on the
    282 			-prconf output.
    283 
    284 	-debug		Extra debugging output.
    285 
    286 	-strace		strace(1) the Xserver process (for troubleshooting.)
    287 	-ltrace		ltrace(1) instead of strace (can be slow.)
    288 
    289 	-h, -help	Print out this help.
    290 
    291 
    292     Xserver-args:
    293 
    294 	Most of the Xorg and XFree86 options will work and are simply
    295 	passed along if you supply them.  Important ones that may be
    296 	supplied if missing:
    297 
    298 	:N		X Display number for server to use.
    299 
    300 	vtNN		Linux virtual terminal (VT) to use (a VT is currently
    301 			still used, just not switched to and from.)
    302 
    303 	-config file		Driver "dummy" tweaked config file, a
    304 	-xf86config file	number of settings are tweaked besides Driver.
    305 
    306 	If -config/-xf86config is not given, the system one
    307 	(e.g. /etc/X11/xorg.conf) is used.  If the system one cannot be
    308 	found, a built-in one is used.	Any settings in the config file
    309 	that are not consistent with "dummy" mode will be overwritten
    310 	(unless -notweak is specified.)
    311 
    312 	Use -config xdummy-builtin to force usage of the builtin config.
    313 
    314 	If "file" is only a basename (e.g. "xorg.dummy.conf") with no /'s,
    315 	then no tweaking of it is done: the X server will look for that
    316 	basename via its normal search algorithm.  If the found file does
    317 	not refer to the "dummy" driver, etc, then the X server will fail.
    318 
    319 Notes:
    320 
    321     The Xorg/XFree86 "dummy" driver is currently undocumented.  It works
    322     well in this mode, but it is evidently not intended for end-users.
    323     So it could be removed or broken at any time.
    324 
    325     If the display Xserver-arg (e.g. :1) is not given, or ":" is given
    326     that indicates $program should try to find a free one (based on
    327     tcp ports.)
    328 
    329     If the display virtual terminal, VT, (e.g. vt9) is not given that
    330     indicates $program should try to find a free one (or guess a high one.) 
    331     
    332     This program is not completely secure WRT files in /tmp (but it tries
    333     to a good degree.)  Better is to use the -tmpdir option to supply a
    334     directory only writable by you.  Even better is to get rid of users
    335     on the local machine you do not trust :-)
    336 
    337     Set XDUMMY_SET_XV=1 to turn on debugging output for this script.
    338 
    339 END
    340 }
    341 
    342 warn() {
    343 	echo "$*" 1>&2
    344 }
    345 
    346 if [ "X$XDUMMY_SET_XV" != "X" ]; then
    347 	set -xv
    348 fi
    349 
    350 if [ "X$XDUMMY_UID" = "X" ]; then
    351 	XDUMMY_UID=`id -u`
    352 	export XDUMMY_UID
    353 fi
    354 if [ "X$XDUMMY_UID" = "X0" ]; then
    355 	if [ "X$SUDO_UID" != "X" ]; then
    356 		XDUMMY_UID=$SUDO_UID
    357 		export XDUMMY_UID
    358 	fi
    359 fi
    360 
    361 # check if root=1 first:
    362 #
    363 if [ "X$XDUMMY_RUN_AS_ROOT" = "X1" ]; then
    364 	root=1
    365 fi
    366 for arg in $*
    367 do
    368 	if [ "X$arg" = "X-nonroot" ]; then
    369 		root=""
    370 	elif [ "X$arg" = "X-root" ]; then
    371 		root=1
    372 	fi
    373 done
    374 
    375 # See if it really needs to be run as root:
    376 #
    377 if [ "X$XDUMMY_SU_EXEC" = "X" -a "X$root" = "X1" -a "X`id -u`" != "X0"  ]; then
    378 	# this is to prevent infinite loop in case su/sudo doesn't work:
    379 	XDUMMY_SU_EXEC=1
    380 	export XDUMMY_SU_EXEC
    381 
    382 	dosu=1
    383 	nosudo=""
    384 
    385 	for arg in $*
    386 	do
    387 		if [ "X$arg" = "X-nonroot" ]; then
    388 			dosu=""
    389 		elif [ "X$arg" = "X-nosudo" ]; then
    390 			nosudo="1"
    391 		elif [ "X$arg" = "X-help" ]; then
    392 			dosu=""
    393 		elif [ "X$arg" = "X-h" ]; then
    394 			dosu=""
    395 		elif [ "X$arg" = "X-install" ]; then
    396 			dosu=""
    397 		elif [ "X$arg" = "X-uninstall" ]; then
    398 			dosu=""
    399 		elif [ "X$arg" = "X-n" ]; then
    400 			dosu=""
    401 		elif [ "X$arg" = "X-prconf" ]; then
    402 			dosu=""
    403 		fi
    404 	done
    405 	if [ $dosu ]; then
    406 		# we need to restart it with su/sudo:
    407 		if type sudo > /dev/null 2>&1; then
    408 			:
    409 		else
    410 			nosudo=1
    411 		fi
    412 		if [ "X$nosudo" = "X" ]; then
    413 			warn "$program: supply the sudo password to restart as root:"
    414 			if [ "X$XDUMMY_UID" != "X" ]; then
    415 				exec sudo $0 -uid $XDUMMY_UID "$@"
    416 			else
    417 				exec sudo $0 "$@"
    418 			fi
    419 		else
    420 			warn "$program: supply the root password to restart as root:"
    421 			if [ "X$XDUMMY_UID" != "X" ]; then
    422 				exec su -c "$0 -uid $XDUMMY_UID $*"
    423 			else
    424 				exec su -c "$0 $*"
    425 			fi
    426 		fi
    427 		# DONE:
    428 		exit
    429 	fi
    430 fi
    431 
    432 # This will hold the X display, e.g. :20
    433 #
    434 disp=""
    435 args=""
    436 cmdline_config=""
    437 
    438 # Process Xdummy args:
    439 #
    440 while [ "X$1" != "X" ]
    441 do
    442     if [ "X$1" = "X-config" -o "X$1" = "X-xf86config" ]; then
    443     	cmdline_config="$2"
    444     fi
    445     case $1 in 
    446 	":"*)	disp=$1
    447                 ;;
    448 	"-install") install=1; runit=""
    449                 ;;
    450 	"-uninstall") uninstall=1; runit=""
    451                 ;;
    452 	"-n")  runit=""
    453                 ;;
    454 	"-no") runit=""
    455                 ;;
    456 	"-norun") runit=""
    457                 ;;
    458 	"-prconf") prconf=1; runit=""
    459                 ;;
    460 	"-notweak") notweak=1
    461                 ;;
    462 	"-noconf")  notweak=1
    463                 ;;
    464 	"-nonroot") root=""
    465                 ;;
    466 	"-root")    root=1
    467                 ;;
    468 	"-nosudo") nosudo=1
    469                 ;;
    470 	"-xserver") xserver="$2"; shift
    471                 ;;
    472 	"-uid") XDUMMY_UID="$2"; shift
    473 		export XDUMMY_UID
    474                 ;;
    475 	"-geom")     geom="$2"; shift
    476                 ;;
    477 	"-geometry") geom="$2"; shift
    478                 ;;
    479 	"-nomodelines") nomodelines=1
    480                 ;;
    481 	"-depth") depth="$2"; args="$args -depth $2";
    482 		  shift
    483                 ;;
    484 	"-DEPTH") depth="$2"; shift
    485                 ;;
    486 	"-tmpdir") XDUMMY_TMPDIR="$2"; shift
    487                 ;;
    488 	"-debug")   debug=1
    489                 ;;
    490 	"-nodebug") debug=""
    491                 ;;
    492 	"-strace") strace=1
    493                 ;;
    494 	"-ltrace") strace=2
    495                 ;;
    496 	"-h")	 help; exit 0
    497                 ;;
    498 	"-help") help; exit 0
    499                 ;;
    500 	*)	args="$args $1"
    501                 ;;
    502     esac
    503     shift
    504 done
    505 
    506 # Try to get a username for use in our tmp directory, etc.
    507 #
    508 user=""
    509 if [ X`id -u` = "X0"  ]; then
    510 	user=root	# this will also be used below for id=0
    511 elif [ "X$USER" != "X" ]; then
    512 	user=$USER
    513 elif [ "X$LOGNAME" != "X" ]; then
    514 	user=$LOGNAME
    515 fi
    516 
    517 # Keep trying...
    518 #
    519 if [ "X$user" = "X" ]; then
    520 	user=`whoami 2>/dev/null`
    521 fi
    522 if [ "X$user" = "X" ]; then
    523 	user=`basename "$HOME"`
    524 fi
    525 if [ "X$user" = "X" -o "X$user" = "X." ]; then
    526 	user="u$$"
    527 fi
    528 
    529 if [ "X$debug" = "X1" -a "X$runit" != "X" ]; then
    530 	echo ""
    531 	echo "/usr/bin/env:"
    532 	env | egrep -v '^(LS_COLORS|TERMCAP)' | sort
    533 	echo ""
    534 fi
    535 
    536 # Function to compile the LD_PRELOAD shared object:
    537 #
    538 make_so() {
    539 	# extract code embedded in this script into a tmp C file: 
    540 	n1=`grep -n '^#code_begin' $0 | head -1 | awk -F: '{print $1}'`
    541 	n2=`grep -n '^#code_end'   $0 | head -1 | awk -F: '{print $1}'`
    542 	n1=`expr $n1 + 1`
    543 	dn=`expr $n2 - $n1`
    544 
    545 	tmp=$tdir/Xdummy.$RANDOM$$.c
    546 	rm -f $tmp
    547 	if [ -e $tmp -o -h $tmp ]; then
    548 		warn "$tmp still exists."
    549 		exit 1
    550 	fi
    551 	touch $tmp || exit 1
    552 	tail -n +$n1 $0 | head -n $dn > $tmp
    553 
    554 	# compile it to Xdummy.so:
    555 	if [ -f "$SO" ]; then
    556 		mv $SO $SO.$$
    557 		rm -f $SO.$$
    558 	fi
    559 	rm -f $SO
    560 	touch $SO
    561 	if [ ! -f "$SO" ]; then
    562 		SO=$tdir/Xdummy.$user.so
    563 		warn "warning switching LD_PRELOAD shared object to: $SO"
    564 	fi
    565 
    566 	if [ -f "$SO" ]; then
    567 		mv $SO $SO.$$
    568 		rm -f $SO.$$
    569 	fi
    570 	rm -f $SO
    571 
    572 	# we assume gcc:
    573 	if [ "X$INTERPOSE_GETUID" = "X1" ]; then
    574 		CFLAGS="$CFLAGS -DINTERPOSE_GETUID"
    575 	fi
    576 	echo "$program:" cc -shared -fPIC $CFLAGS -o $SO $tmp
    577                          cc -shared -fPIC $CFLAGS -o $SO $tmp
    578 	rc=$?
    579 	rm -f $tmp
    580 	if [ $rc != 0 ]; then
    581 		warn "$program: cannot build $SO"
    582 		exit 1
    583 	fi
    584 	if [ "X$debug" != "X" -o "X$install" != "X" ]; then
    585 		warn "$program: created  $SO"
    586 		ls -l "$SO"
    587 	fi
    588 }
    589 
    590 # Set tdir to tmp dir for make_so():
    591 if [ "X$XDUMMY_TMPDIR" != "X" ]; then
    592 	tdir=$XDUMMY_TMPDIR
    593 	mkdir -p $tdir
    594 else
    595 	tdir="/tmp"
    596 fi
    597 
    598 # Handle -install/-uninstall case:
    599 SO=$0.so
    600 if [ "X$install" != "X" -o "X$uninstall" != "X" ]; then
    601 	if [ -e "$SO" -o -h "$SO" ]; then
    602 		warn "$program: removing $SO"
    603 	fi
    604 	if [ -f "$SO" ]; then
    605 		mv $SO $SO.$$
    606 		rm -f $SO.$$
    607 	fi
    608 	rm -f $SO
    609 	if [ -e "$SO" -o -h "$SO" ]; then
    610 		warn "warning: $SO still exists."
    611 		exit 1
    612 	fi
    613 	if [ $install ]; then
    614 		make_so
    615 		if [ ! -f "$SO" ]; then
    616 			exit 1
    617 		fi
    618 	fi
    619 	exit 0
    620 fi
    621 
    622 # We need a tmp directory for the .so, tweaked config file, and for
    623 # redirecting filenames we cannot create (under -nonroot)
    624 #
    625 tack=""
    626 if [ "X$XDUMMY_TMPDIR" = "X" ]; then
    627 	XDUMMY_TMPDIR="/tmp/Xdummy.$user"
    628 
    629 	# try to tack on a unique subdir (display number or pid)
    630 	# to allow multiple instances
    631 	#
    632 	if [ "X$disp" != "X" ]; then
    633 		t0=$disp
    634 	else
    635 		t0=$1
    636 	fi
    637 	tack=`echo "$t0" | sed -e 's/^.*://'`
    638 	if echo "$tack" | grep '^[0-9][0-9]*$' > /dev/null; then
    639 		:
    640 	else
    641 		tack=$$
    642 	fi
    643 	if [ "X$tack" != "X" ]; then
    644 		XDUMMY_TMPDIR="$XDUMMY_TMPDIR/$tack"
    645 	fi
    646 fi
    647 
    648 tmp=$XDUMMY_TMPDIR
    649 if echo "$tmp" | grep '^/tmp' > /dev/null; then
    650 	if [ "X$tmp" != "X/tmp" -a "X$tmp" != "X/tmp/" ]; then
    651 		# clean this subdir of /tmp out, otherwise leave it...
    652 		rm -rf $XDUMMY_TMPDIR
    653 		if [ -e $XDUMMY_TMPDIR ]; then
    654 			warn "$XDUMMY_TMPDIR still exists"
    655 			exit 1
    656 		fi
    657 	fi
    658 fi
    659 
    660 mkdir -p $XDUMMY_TMPDIR
    661 chmod 700 $XDUMMY_TMPDIR
    662 if [ "X$tack" != "X" ]; then
    663 	chmod 700 `dirname "$XDUMMY_TMPDIR"` 2>/dev/null
    664 fi
    665 
    666 # See if we can write something there:
    667 #
    668 tfile="$XDUMMY_TMPDIR/test.file"
    669 touch $tfile
    670 if [ ! -f "$tfile" ]; then
    671 	XDUMMY_TMPDIR="/tmp/Xdummy.$$.$USER"
    672 	warn "warning: setting tmpdir to $XDUMMY_TMPDIR ..."
    673 	rm -rf $XDUMMY_TMPDIR || exit 1
    674 	mkdir -p $XDUMMY_TMPDIR || exit 1
    675 fi
    676 rm -f $tfile
    677 
    678 export XDUMMY_TMPDIR
    679 
    680 # Compile the LD_PRELOAD shared object if needed (needs XDUMMY_TMPDIR)
    681 #
    682 if [ ! -f "$SO" ]; then
    683 	SO="$XDUMMY_TMPDIR/Xdummy.so"
    684 	make_so
    685 fi
    686 
    687 # Decide which X server to use:
    688 #
    689 if [ "X$xserver" = "X" ]; then
    690 	if type Xorg >/dev/null 2>&1; then
    691 		xserver="Xorg"
    692 	elif type XFree86 >/dev/null 2>&1; then
    693 		xserver="XFree86"
    694 	elif -x /usr/bin/Xorg; then
    695 		xserver="/usr/bin/Xorg"
    696 	elif -x /usr/X11R6/bin/Xorg; then
    697 		xserver="/usr/X11R6/bin/Xorg"
    698 	elif -x /usr/X11R6/bin/XFree86; then
    699 		xserver="/usr/X11R6/bin/XFree86"
    700 	fi
    701 	if [ "X$xserver" = "X" ]; then
    702 		# just let it fail below.
    703 		xserver="/usr/bin/Xorg"
    704 		warn "$program: cannot locate a stock Xserver... assuming $xserver"
    705 	fi
    706 fi
    707 
    708 # See if the binary is suid or not readable under -nonroot mode:
    709 #
    710 if [ "X$BASH_VERSION" != "X" ]; then
    711 	xserver_path=`type -p $xserver 2>/dev/null`
    712 else
    713 	xserver_path=`type $xserver 2>/dev/null | awk '{print $NF}'`
    714 fi
    715 if [ -e "$xserver_path" -a "X$root" = "X" -a "X$runit" != "X" ]; then
    716 	if [ ! -r $xserver_path -o -u $xserver_path -o -g $xserver_path ]; then
    717 		# XXX not quite correct with rm -rf $XDUMMY_TMPDIR ...
    718 		# we keep on a filesystem we know root can write to.
    719 		base=`basename "$xserver_path"`
    720 		new="/tmp/$base.$user.bin"
    721 		if [ -e $new ]; then
    722 			snew=`ls -l $new          | awk '{print $5}' | grep '^[0-9][0-9]*$'`
    723 			sold=`ls -l $xserver_path | awk '{print $5}' | grep '^[0-9][0-9]*$'`
    724 			if [ "X$snew" != "X" -a "X$sold" != "X" -a "X$sold" != "X$snew" ]; then
    725 				warn "removing different sized copy:"
    726 				ls -l $new $xserver_path
    727 				rm -f $new
    728 			fi
    729 		fi
    730 		if [ ! -e $new -o ! -s $new ]; then
    731 			rm -f $new
    732 			touch $new || exit 1
    733 			chmod 700 $new || exit 1
    734 			if [ ! -r $xserver_path ]; then
    735 				warn ""
    736 				warn "NEED TO COPY UNREADABLE $xserver_path to $new as root:"
    737 				warn ""
    738 				ls -l $xserver_path 1>&2
    739 				warn ""
    740 				warn "This only needs to be done once:"
    741 				warn "    cat $xserver_path > $new"
    742 				warn ""
    743 				nos=$nosudo
    744 				if type sudo > /dev/null 2>&1; then
    745 					:
    746 				else
    747 					nos=1
    748 				fi
    749 				if [ "X$nos" = "X1" ]; then
    750 					warn "Please supply root passwd to 'su -c'"
    751 					su -c "cat $xserver_path > $new"
    752 				else
    753 					warn "Please supply the sudo passwd if asked:"
    754 					sudo /bin/sh -c "cat $xserver_path > $new"
    755 				fi
    756 			else
    757 				warn ""
    758 				warn "COPYING SETUID $xserver_path to $new"
    759 				warn ""
    760 				ls -l $xserver_path 1>&2
    761 				warn ""
    762 				cat $xserver_path > $new
    763 			fi
    764 			ls -l $new
    765 			if [ -s $new ]; then
    766 				:
    767 			else
    768 				rm -f $new
    769 				ls -l $new
    770 				exit 1
    771 			fi
    772 			warn ""
    773 			warn "Please restart Xdummy now."
    774 			exit 0
    775 		fi
    776 		if [ ! -O $new ]; then
    777 			warn "file \"$new\" not owned by us!"
    778 			ls -l $new
    779 			exit 1
    780 		fi
    781 		xserver=$new
    782 	fi 
    783 fi
    784 
    785 # Work out display:
    786 #
    787 if [ "X$disp" != "X" ]; then
    788 	:
    789 elif [ "X$1" != "X" ]; then
    790 	if echo "$1" | grep '^:[0-9]' > /dev/null; then
    791 		disp=$1
    792 		shift
    793 	elif [ "X$1" = "X:" ]; then
    794 		# ":" means for us to find one.
    795 		shift
    796 	fi
    797 fi
    798 if [ "X$disp" = "X" -o "X$disp" = "X:" ]; then
    799 	# try to find an open display port:
    800 	# (tcp outdated...)
    801 	ports=`netstat -ant | grep LISTEN | awk '{print $4}' | sed -e 's/^.*://'`
    802 	n=0
    803 	while [ $n -le 20 ]
    804 	do
    805 		port=`printf "60%02d" $n`
    806 		if echo "$ports" | grep "^${port}\$" > /dev/null; then
    807 			:
    808 		else
    809 			disp=":$n"
    810 			warn "$program: auto-selected DISPLAY $disp"
    811 			break	
    812 		fi
    813 		n=`expr $n + 1`
    814 	done
    815 fi
    816 
    817 # Work out which vt to use, try to find/guess an open one if necessary.
    818 #
    819 vt=""
    820 for arg in $*
    821 do
    822 	if echo "$arg" | grep '^vt' > /dev/null; then
    823 		vt=$arg
    824 		break
    825 	fi
    826 done
    827 if [ "X$vt" = "X" ]; then
    828 	if [ "X$user" = "Xroot" ]; then
    829 		# root can user fuser(1) to see if it is in use:
    830 		if type fuser >/dev/null 2>&1; then
    831 			# try /dev/tty17 thru /dev/tty32
    832 			n=17
    833 			while [ $n -le 32 ]
    834 			do
    835 				dev="/dev/tty$n"
    836 				if fuser $dev >/dev/null 2>&1; then
    837 					:
    838 				else
    839 					vt="vt$n"
    840 					warn "$program: auto-selected VT $vt => $dev"
    841 					break
    842 				fi
    843 				n=`expr $n + 1`
    844 			done
    845 		fi
    846 	fi
    847 	if [ "X$vt" = "X" ]; then
    848 		# take a wild guess...
    849 		vt=vt16
    850 		warn "$program: selected fallback VT $vt"
    851 	fi
    852 else
    853 	vt=""
    854 fi
    855 
    856 # Decide flavor of Xserver:
    857 #
    858 stype=`basename "$xserver"`
    859 if echo "$stype" | grep -i xfree86 > /dev/null; then
    860 	stype=xfree86
    861 else
    862 	stype=xorg
    863 fi
    864 
    865 tweak_config() {
    866     in="$1"
    867     config2="$XDUMMY_TMPDIR/xdummy_modified_xconfig.conf"
    868     if [ "X$disp" != "X" ]; then
    869     	d=`echo "$disp" | sed -e 's,/,,g' -e 's/:/_/g'`
    870 	config2="$config2$d"
    871     fi
    872     
    873     # perl script to tweak the config file... add/delete options, etc.
    874     #
    875     env XDUMMY_GEOM=$geom \
    876         XDUMMY_DEPTH=$depth \
    877         XDUMMY_NOMODELINES=$nomodelines \
    878         perl > $config2 < $in -e '
    879     $n = 0;
    880     $geom  = $ENV{XDUMMY_GEOM};
    881     $depth = $ENV{XDUMMY_DEPTH};
    882     $nomodelines = $ENV{XDUMMY_NOMODELINES};
    883     $mode_str = "";
    884     $videoram = "24000";
    885     $HorizSync   = "30.0 - 130.0";
    886     $VertRefresh = "50.0 - 250.0";
    887     if ($geom ne "") {
    888     	my $tmp = "";
    889 	foreach $g (split(/,/, $geom)) {
    890 		$tmp .= "\"$g\" ";
    891 		if (!$nomodelines && $g =~ /(\d+)x(\d+)/) {
    892 			my $w = $1;
    893 			my $h = $2;
    894 			$mode_str .= "  Modeline \"$g\" ";
    895 			my $dot = sprintf("%.2f", $w * $h * 70 * 1.e-6);
    896 			$mode_str .= $dot;
    897 			$mode_str .= " " . $w;
    898 			$mode_str .= " " . int(1.02 * $w);
    899 			$mode_str .= " " . int(1.10 * $w);
    900 			$mode_str .= " " . int(1.20 * $w);
    901 			$mode_str .= " " . $h;
    902 			$mode_str .= " " . int($h + 1);
    903 			$mode_str .= " " . int($h + 3);
    904 			$mode_str .= " " . int($h + 20);
    905 			$mode_str .= "\n";
    906 		}
    907 	}
    908 	$tmp =~ s/\s*$//;
    909 	$geom = $tmp;
    910     }
    911     while (<>) {
    912 	if ($ENV{XDUMMY_NOTWEAK}) {
    913 		print $_;
    914 		next;
    915 	}
    916 	$n++;
    917 	if (/^\s*#/) {
    918 		# pass comments straight thru
    919 		print;
    920 		next;
    921 	}
    922 	if (/^\s*Section\s+(\S+)/i) {
    923 		# start of Section
    924 		$sect = $1;
    925 		$sect =~ s/\W//g;
    926 		$sect =~ y/A-Z/a-z/;
    927 		$sects{$sect} = 1;
    928 		print;
    929 		next;
    930 	}
    931 	if (/^\s*EndSection/i) {
    932 		# end of Section
    933 		if ($sect eq "serverflags") {
    934 			if (!$got_DontVTSwitch) {
    935 				print "  ##Xdummy:##\n";
    936 				print "  Option \"DontVTSwitch\" \"true\"\n";
    937 			}
    938 			if (!$got_AllowMouseOpenFail) {
    939 				print "  ##Xdummy:##\n";
    940 				print "  Option \"AllowMouseOpenFail\" \"true\"\n";
    941 			}
    942 			if (!$got_PciForceNone) {
    943 				print "  ##Xdummy:##\n";
    944 				print "  Option \"PciForceNone\" \"true\"\n";
    945 			}
    946 		} elsif ($sect eq "device") {
    947 			if (!$got_Driver) {
    948 				print "  ##Xdummy:##\n";
    949 				print "  Driver \"dummy\"\n";
    950 			}
    951 			if (!$got_VideoRam) {
    952 				print "  ##Xdummy:##\n";
    953 				print "  VideoRam $videoram\n";
    954 			}
    955 		} elsif ($sect eq "screen") {
    956 			if ($depth ne "" && !got_DefaultDepth) {
    957 				print "  ##Xdummy:##\n";
    958 				print "  DefaultDepth $depth\n";
    959 			}
    960 			if ($got_Monitor eq "") {
    961 				print "  ##Xdummy:##\n";
    962 				print "  Monitor \"Monitor0\"\n";
    963 			}
    964 		} elsif ($sect eq "monitor") {
    965 			if (!got_HorizSync) {
    966 				print "  ##Xdummy:##\n";
    967 				print "  HorizSync   $HorizSync\n";
    968 			}
    969 			if (!got_VertRefresh) {
    970 				print "  ##Xdummy:##\n";
    971 				print "  VertRefresh $VertRefresh\n";
    972 			}
    973 			if (!$nomodelines) {
    974 				print "  ##Xdummy:##\n";
    975 				print $mode_str;
    976 			}
    977 		}
    978 		$sect = "";
    979 		print;
    980 		next;
    981 	}
    982 
    983 	if (/^\s*SubSection\s+(\S+)/i) {
    984 		# start of Section
    985 		$subsect = $1;
    986 		$subsect =~ s/\W//g;
    987 		$subsect =~ y/A-Z/a-z/;
    988 		$subsects{$subsect} = 1;
    989 		if ($sect eq "screen" && $subsect eq "display") {
    990 			$got_Modes = 0;
    991 		}
    992 		print;
    993 		next;
    994 	}
    995 	if (/^\s*EndSubSection/i) {
    996 		# end of SubSection
    997 		if ($sect eq "screen") {
    998 			if ($subsect eq "display") {
    999 				if ($depth ne "" && !$set_Depth) {
   1000 					print "          ##Xdummy:##\n";
   1001 					print "          Depth\t$depth\n";
   1002 				}
   1003 				if ($geom ne "" && ! $got_Modes) {
   1004 					print "          ##Xdummy:##\n";
   1005 					print "          Modes\t$geom\n";
   1006 				}
   1007 			}
   1008 		}
   1009 		$subsect = "";
   1010 		print;
   1011 		next;
   1012 	}
   1013 
   1014 	$l = $_;
   1015 	$l =~ s/#.*$//;
   1016 	if ($sect eq "serverflags") {
   1017 		if ($l =~ /^\s*Option.*DontVTSwitch/i) {
   1018 			$_ =~ s/false/true/ig;
   1019 			$got_DontVTSwitch = 1;
   1020 		}
   1021 		if ($l =~ /^\s*Option.*AllowMouseOpenFail/i) {
   1022 			$_ =~ s/false/true/ig;
   1023 			$got_AllowMouseOpenFail = 1;
   1024 		}
   1025 		if ($l =~ /^\s*Option.*PciForceNone/i) {
   1026 			$_ =~ s/false/true/ig;
   1027 			$got_PciForceNone= 1;
   1028 		}
   1029 	}
   1030 	if ($sect eq "module") {
   1031 		if ($l =~ /^\s*Load.*\b(dri|fbdevhw)\b/i) {
   1032 			$_ = "##Xdummy## $_";
   1033 		}
   1034 	}
   1035 	if ($sect eq "monitor") {
   1036 		if ($l =~ /^\s*HorizSync/i) {
   1037 			$got_HorizSync = 1;
   1038 		}
   1039 		if ($l =~ /^\s*VertRefresh/i) {
   1040 			$got_VertRefresh = 1;
   1041 		}
   1042 	}
   1043 	if ($sect eq "device") {
   1044 		if ($l =~ /^(\s*Driver)\b/i) {
   1045 			$_ = "$1 \"dummy\"\n";
   1046 			$got_Driver = 1;
   1047 		}
   1048 		if ($l =~ /^\s*VideoRam/i) {
   1049 			$got_VideoRam= 1;
   1050 		}
   1051 	}
   1052 	if ($sect eq "inputdevice") {
   1053 		if ($l =~ /^\s*Option.*\bDevice\b/i) {
   1054 			print "  ##Xdummy:##\n";
   1055 			$_ = "  Option \"Device\" \"/dev/dilbert$n\"\n";
   1056 		}
   1057 	}
   1058 	if ($sect eq "screen") {
   1059 		if ($l =~ /^\s*DefaultDepth\s+(\d+)/i) {
   1060 			if ($depth ne "") {
   1061 				print "  ##Xdummy:##\n";
   1062 				$_ = "  DefaultDepth\t$depth\n";
   1063 			}
   1064 			$got_DefaultDepth = 1;
   1065 		}
   1066 		if ($l =~ /^\s*Monitor\s+(\S+)/i) {
   1067 			$got_Monitor = $1;
   1068 			$got_Monitor =~ s/"//g;
   1069 		}
   1070 		if ($subsect eq "display") {
   1071 			if ($geom ne "") {
   1072 				if ($l =~ /^(\s*Modes)\b/i) {
   1073 					print "          ##Xdummy:##\n";
   1074 					$_ = "$1 $geom\n";
   1075 					$got_Modes = 1;
   1076 				}
   1077 			}
   1078 			if ($l =~ /^\s*Depth\s+(\d+)/i) {
   1079 				my $d = $1;
   1080 				if (!$set_Depth && $depth ne "") {
   1081 					$set_Depth = 1;
   1082 					if ($depth != $d) {
   1083 						print "          ##Xdummy:##\n";
   1084 						$_ =  "          Depth\t$depth\n";
   1085 					}
   1086 				}
   1087 			}
   1088 		}
   1089 	}
   1090 	print;
   1091     }
   1092     if ($ENV{XDUMMY_NOTWEAK}) {
   1093 	exit;
   1094     }
   1095     # create any crucial sections that are missing:
   1096     if (! exists($sects{serverflags})) {
   1097 	print "\n##Xdummy:##\n";
   1098     	print "Section \"ServerFlags\"\n";
   1099     	print "  Option \"DontVTSwitch\" \"true\"\n";
   1100     	print "  Option \"AllowMouseOpenFail\" \"true\"\n";
   1101     	print "  Option \"PciForceNone\" \"true\"\n";
   1102     	print "EndSection\n";
   1103     }
   1104     if (! exists($sects{device})) {
   1105 	print "\n##Xdummy:##\n";
   1106     	print "Section \"Device\"\n";
   1107     	print "  Identifier \"Videocard0\"\n";
   1108     	print "  Driver \"dummy\"\n";
   1109 	print "  VideoRam $videoram\n";
   1110     	print "EndSection\n";
   1111     }
   1112     if (! exists($sects{monitor})) {
   1113 	print "\n##Xdummy:##\n";
   1114     	print "Section \"Monitor\"\n";
   1115     	print "  Identifier \"Monitor0\"\n";
   1116     	print "  HorizSync   $HorizSync\n";
   1117     	print "  VertRefresh $VertRefresh\n";
   1118     	print "EndSection\n";
   1119     }
   1120     if (! exists($sects{screen})) {
   1121 	print "\n##Xdummy:##\n";
   1122     	print "Section \"Screen\"\n";
   1123     	print "  Identifier \"Screen0\"\n";
   1124     	print "  Device \"Videocard0\"\n";
   1125 	if ($got_Monitor ne "") {
   1126     		print "  Monitor \"$got_Monitor\"\n";
   1127 	} else {
   1128     		print "  Monitor \"Monitor0\"\n";
   1129 	}
   1130 	if ($depth ne "") {
   1131     		print "  DefaultDepth $depth\n";
   1132 	} else {
   1133     		print "  DefaultDepth 24\n";
   1134 	}
   1135     	print "  SubSection \"Display\"\n";
   1136     	print "    Viewport 0 0\n";
   1137     	print "    Depth 24\n";
   1138 	if ($got_Modes) {
   1139 		;
   1140 	} elsif ($geom ne "") {
   1141     		print "    Modes $geom\n";
   1142 	} else {
   1143     		print "    Modes \"1280x1024\" \"1024x768\" \"800x600\"\n";
   1144 	}
   1145     	print "  EndSubSection\n";
   1146     	print "EndSection\n";
   1147     }
   1148 ';
   1149 }
   1150 
   1151 # Work out config file and tweak it.
   1152 #
   1153 if [ "X$cmdline_config" = "X" ]; then
   1154 	:
   1155 elif [ "X$cmdline_config" = "Xxdummy-builtin" ]; then
   1156 	:
   1157 elif echo "$cmdline_config" | grep '/' > /dev/null; then
   1158 	:
   1159 else
   1160 	# ignore basename only case (let server handle it)
   1161 	cmdline_config=""
   1162 	notweak=1
   1163 fi
   1164 
   1165 config=$cmdline_config
   1166 
   1167 if [ "X$notweak" = "X1" -a "X$root" = "X" -a  -f "$cmdline_config" ]; then
   1168 	# if not root we need to copy (but not tweak) the specified config.
   1169 	XDUMMY_NOTWEAK=1
   1170 	export XDUMMY_NOTWEAK
   1171 	notweak=""
   1172 fi
   1173 
   1174 if [ ! $notweak ]; then
   1175 	# tweaked config will be put in $config2:
   1176 	config2=""
   1177 	if [ "X$config" = "X" ]; then
   1178 		# use the default one:
   1179 		if [ "X$stype" = "Xxorg" ]; then
   1180 			config=/etc/X11/xorg.conf
   1181 		else
   1182 			if [ -f "/etc/X11/XF86Config-4" ]; then
   1183 				config="/etc/X11/XF86Config-4"
   1184 			else
   1185 				config="/etc/X11/XF86Config"
   1186 			fi
   1187 		fi
   1188 		if [ ! -f "$config" ]; then
   1189 			for c in /etc/X11/xorg.conf /etc/X11/XF86Config-4 /etc/X11/XF86Config
   1190 			do
   1191 				if [ -f $c ]; then
   1192 					config=$c
   1193 					break
   1194 				fi
   1195 			done
   1196 		fi
   1197 	fi
   1198 
   1199 	if [ "X$config" = "Xxdummy-builtin" ]; then
   1200 		config=""
   1201 	fi
   1202 
   1203 	if [ ! -f "$config" ]; then
   1204 		config="$XDUMMY_TMPDIR/xorg.conf"
   1205 		warn "$program: using minimal built-in xorg.conf settings."
   1206 		cat > $config <<END
   1207 
   1208 Section "ServerLayout"
   1209     Identifier     "Layout0"
   1210     Screen      0  "Screen0"
   1211     InputDevice    "Keyboard0" "CoreKeyboard"
   1212     InputDevice    "Mouse0" "CorePointer"
   1213 EndSection
   1214 
   1215 Section "Files"
   1216 EndSection
   1217 
   1218 Section "Module"
   1219     Load           "dbe"
   1220     Load           "extmod"
   1221     Load           "freetype"
   1222     Load           "glx"
   1223 EndSection
   1224 
   1225 Section "InputDevice"
   1226     Identifier     "Mouse0"
   1227     Driver         "mouse"
   1228     Option         "Protocol" "auto"
   1229     Option         "Device" "/dev/psaux"
   1230     Option         "Emulate3Buttons" "no"
   1231     Option         "ZAxisMapping" "4 5"
   1232 EndSection
   1233 
   1234 Section "InputDevice"
   1235     Identifier     "Keyboard0"
   1236     Driver         "kbd"
   1237 EndSection
   1238 
   1239 Section "Monitor"
   1240     Identifier     "Monitor0"
   1241     VendorName     "Unknown"
   1242     ModelName      "Unknown"
   1243     HorizSync       30.0 - 130.0
   1244     VertRefresh     50.0 - 250.0
   1245     Option         "DPMS"
   1246 EndSection
   1247 
   1248 Section "Device"
   1249     Identifier     "Device0"
   1250     Driver         "foovideo"
   1251     VendorName     "foovideo Corporation"
   1252 EndSection
   1253 
   1254 Section "Screen"
   1255     Identifier     "Screen0"
   1256     Device         "Device0"
   1257     Monitor        "Monitor0"
   1258     DefaultDepth    24
   1259     SubSection     "Display"
   1260         Depth       24
   1261         Modes           "1280x1024"
   1262     EndSubSection
   1263 EndSection
   1264 
   1265 END
   1266 	fi
   1267 
   1268 	if [ -f "$config" ]; then
   1269 		tweak_config $config
   1270 	fi
   1271 
   1272 	# now we need to get our tweaked config file onto the command line:
   1273 	if [ "X$cmdline_config" = "X" ]; then
   1274 		# append to cmdline (FUBAR will be substituted below.)
   1275 		if [ "X$stype" = "Xxorg" ]; then
   1276 			args="$args -config FUBAR"
   1277 		else
   1278 			args="$args -xf86config FUBAR"
   1279 		fi
   1280 	fi
   1281 	if [ "X$config2" != "X" ]; then
   1282 		# or modify $args:
   1283 		c2=$config2
   1284 		if [ "X$root" = "X" ]; then
   1285 			# ordinary user cannot use absolute path.
   1286 			c2=`basename $config2`
   1287 		fi
   1288 		args=`echo "$args" | sed \
   1289 			-e "s,-config  *[^ ][^ ]*,-config $c2,g" \
   1290 			-e "s,-xf86config  *[^ ][^ ]*,-xf86config $c2,g"`
   1291 	fi
   1292 fi
   1293 
   1294 if [ $prconf ]; then
   1295 	warn ""
   1296 	warn "Printing out the Xorg/XFree86 server config file:"
   1297 	warn ""
   1298 	if [ "X$config2" = "X" ]; then
   1299 		warn "NO CONFIG GENERATED."
   1300 		exit 1
   1301 	else
   1302 		cat "$config2"
   1303 	fi
   1304 	exit 0
   1305 fi
   1306 
   1307 if [ $debug ]; then
   1308 	XDUMMY_DEBUG=1
   1309 	export XDUMMY_DEBUG
   1310 fi
   1311 if [ $root ]; then
   1312 	XDUMMY_ROOT=1
   1313 	export XDUMMY_ROOT
   1314 fi
   1315 
   1316 # Finally, run it:
   1317 #
   1318 if [ "X$debug" != "X" -o "X$runit" = "X" ]; then
   1319 	if [ ! $runit ]; then
   1320 		echo ""
   1321 		echo "/usr/bin/env:"
   1322 		env | egrep -v '^(LS_COLORS|TERMCAP)' | sort
   1323 		echo ""
   1324 		echo "XDUMMY*:"
   1325 		env | grep '^XDUMMY' | sort
   1326 		echo ""
   1327 	fi
   1328 	warn ""
   1329 	warn "The command to run is:"
   1330 	warn ""
   1331 	so=$SO
   1332 	pwd=`pwd`
   1333 	if echo "$so" | grep '^\./' > /dev/null; then
   1334 		so=`echo "$so" | sed -e "s,^\.,$pwd,"`
   1335 	fi
   1336 	if echo "$so" | grep '/' > /dev/null; then
   1337 		:
   1338 	else
   1339 		so="$pwd/$so"
   1340 	fi
   1341 	warn "env LD_PRELOAD=$so $xserver $disp $args $vt"
   1342 	warn ""
   1343 	if [ ! $runit ]; then
   1344 		exit 0
   1345 	fi
   1346 fi
   1347 
   1348 if [ $strace ]; then
   1349 	if [ "X$strace" = "X2" ]; then
   1350 		ltrace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
   1351 	else
   1352 		strace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
   1353 	fi
   1354 else
   1355 	exec env LD_PRELOAD=$SO $xserver $disp $args $vt
   1356 fi
   1357 
   1358 exit $?
   1359 
   1360 #########################################################################
   1361 
   1362 code() {
   1363 #code_begin
   1364 #include <stdio.h>
   1365 #define O_ACCMODE          0003
   1366 #define O_RDONLY             00
   1367 #define O_WRONLY             01
   1368 #define O_RDWR               02
   1369 #define O_CREAT            0100 /* not fcntl */
   1370 #define O_EXCL             0200 /* not fcntl */
   1371 #define O_NOCTTY           0400 /* not fcntl */
   1372 #define O_TRUNC           01000 /* not fcntl */
   1373 #define O_APPEND          02000
   1374 #define O_NONBLOCK        04000
   1375 #define O_NDELAY        O_NONBLOCK
   1376 #define O_SYNC           010000
   1377 #define O_FSYNC          O_SYNC
   1378 #define O_ASYNC          020000
   1379 
   1380 #include <unistd.h>
   1381 #include <stdlib.h>
   1382 #include <string.h>
   1383 
   1384 #include <linux/vt.h>
   1385 #include <linux/kd.h>
   1386 
   1387 #define __USE_GNU
   1388 #include <dlfcn.h>
   1389 
   1390 static char tmpdir[4096];
   1391 static char str1[4096];
   1392 static char str2[4096];
   1393 
   1394 static char devs[256][1024];
   1395 static int debug = -1;
   1396 static int root = -1;
   1397 static int changed_uid = 0;
   1398 static int saw_fonts = 0;
   1399 static int saw_lib_modules = 0;
   1400 
   1401 static time_t start = 0; 
   1402 
   1403 void check_debug(void) {
   1404 	if (debug < 0) {
   1405 		if (getenv("XDUMMY_DEBUG") != NULL) {
   1406 			debug = 1;
   1407 		} else {
   1408 			debug = 0;
   1409 		}
   1410 		/* prevent other processes using the preload: */
   1411 		putenv("LD_PRELOAD=");
   1412 	}
   1413 }
   1414 void check_root(void) {
   1415 	if (root < 0) {
   1416 		/* script tells us if we are root */
   1417 		if (getenv("XDUMMY_ROOT") != NULL) {
   1418 			root = 1;
   1419 		} else {
   1420 			root = 0;
   1421 		}
   1422 	}
   1423 }
   1424 
   1425 void check_uid(void) {
   1426 	if (start == 0) {
   1427 		start = time(NULL);
   1428 		if (debug) fprintf(stderr, "START: %u\n", (unsigned int) start);
   1429 		return;
   1430 	} else if (changed_uid == 0) {
   1431 		if (saw_fonts || time(NULL) > start + 20) {
   1432 			if (getenv("XDUMMY_UID")) {
   1433 				int uid = atoi(getenv("XDUMMY_UID"));
   1434 				if (debug) fprintf(stderr, "SETREUID: %d saw_fonts=%d\n", uid, saw_fonts);
   1435 				if (uid >= 0) {
   1436 					/* this will simply fail in -nonroot mode: */
   1437 					setreuid(uid, -1);
   1438 				}
   1439 			}
   1440 			changed_uid = 1;
   1441 		}
   1442 	}
   1443 }
   1444 
   1445 #define CHECKIT if (debug < 0) check_debug(); \
   1446 		if (root  < 0) check_root(); \
   1447 		check_uid();
   1448 
   1449 static void set_tmpdir(void) {
   1450 	char *s;
   1451 	static int didset = 0;
   1452 	if (didset) {
   1453 		return;
   1454 	}
   1455 	s = getenv("XDUMMY_TMPDIR");
   1456 	if (! s) {
   1457 		s = "/tmp";
   1458 	}
   1459 	tmpdir[0] = '\0';
   1460 	strcat(tmpdir, s);
   1461 	strcat(tmpdir, "/");
   1462 	didset = 1;
   1463 }
   1464 
   1465 static char *tmpdir_path(const char *path) {
   1466 	char *str;
   1467 	set_tmpdir();
   1468 	strcpy(str2, path);
   1469 	str = str2;
   1470 	while (*str) {
   1471 		if (*str == '/') {
   1472 			*str = '_';
   1473 		}
   1474 		str++;
   1475 	}
   1476 	strcpy(str1, tmpdir);
   1477 	strcat(str1, str2);
   1478 	return str1;
   1479 }
   1480 
   1481 int open(const char *pathname, int flags, unsigned short mode) {
   1482 	int fd;
   1483 	char *store_dev = NULL;
   1484 	static int (*real_open)(const char *, int , unsigned short) = NULL;
   1485 
   1486 	CHECKIT
   1487 	if (! real_open) {
   1488 		real_open = (int (*)(const char *, int , unsigned short))
   1489 			dlsym(RTLD_NEXT, "open");
   1490 	}
   1491 
   1492 	if (strstr(pathname, "lib/modules/")) {
   1493 		/* not currently used. */
   1494 		saw_lib_modules = 1;
   1495 	}
   1496 
   1497 	if (!root) {
   1498 		if (strstr(pathname, "/dev/") == pathname) {
   1499 			store_dev = strdup(pathname);
   1500 		}
   1501 		if (strstr(pathname, "/dev/tty") == pathname && strcmp(pathname, "/dev/tty")) {
   1502 			pathname = tmpdir_path(pathname);
   1503 			if (debug) fprintf(stderr, "OPEN: %s -> %s (as FIFO)\n", store_dev, pathname);
   1504 			/* we make it a FIFO so ioctl on it does not fail */
   1505 			unlink(pathname);
   1506 			mkfifo(pathname, 0666);
   1507 		} else if (0) {
   1508 			/* we used to handle more /dev files ... */
   1509 			fd = real_open(pathname, O_WRONLY|O_CREAT, 0777);
   1510 			close(fd);
   1511 		}
   1512 	}
   1513 
   1514 	fd = real_open(pathname, flags, mode);
   1515 
   1516 	if (debug) fprintf(stderr, "OPEN: %s %d %d fd=%d\n", pathname, flags, mode, fd);
   1517 
   1518 	if (! root) {
   1519 		if (store_dev) {
   1520 			if (fd < 256) {
   1521 				strcpy(devs[fd], store_dev);
   1522 			}
   1523 			free(store_dev);
   1524 		}
   1525 	}
   1526 
   1527 	return(fd);
   1528 }
   1529 
   1530 int open64(const char *pathname, int flags, unsigned short mode) {
   1531 	int fd;
   1532 
   1533 	CHECKIT
   1534 	if (debug) fprintf(stderr, "OPEN64: %s %d %d\n", pathname, flags, mode);
   1535 
   1536 	fd = open(pathname, flags, mode);
   1537 	return(fd);
   1538 }
   1539 
   1540 int rename(const char *oldpath, const char *newpath) {
   1541 	static int (*real_rename)(const char *, const char *) = NULL;
   1542 
   1543 	CHECKIT
   1544 	if (! real_rename) {
   1545 		real_rename = (int (*)(const char *, const char *))
   1546 			dlsym(RTLD_NEXT, "rename");
   1547 	}
   1548 
   1549 	if (debug) fprintf(stderr, "RENAME: %s %s\n", oldpath, newpath);
   1550 
   1551 	if (root) {
   1552 		return(real_rename(oldpath, newpath));
   1553 	}
   1554 
   1555 	if (strstr(oldpath, "/var/log") == oldpath) {
   1556 		if (debug) fprintf(stderr, "RENAME: returning 0\n");
   1557 		return 0;
   1558 	}
   1559 	return(real_rename(oldpath, newpath));
   1560 }
   1561 
   1562 FILE *fopen(const char *pathname, const char *mode) {
   1563 	static FILE* (*real_fopen)(const char *, const char *) = NULL;
   1564 	char *str;
   1565 
   1566 	if (! saw_fonts) {
   1567 		if (strstr(pathname, "/fonts/")) {
   1568 			if (strstr(pathname, "fonts.dir")) {
   1569 				saw_fonts = 1;
   1570 			} else if (strstr(pathname, "fonts.alias")) {
   1571 				saw_fonts = 1;
   1572 			}
   1573 		}
   1574 	}
   1575 
   1576 	CHECKIT
   1577 	if (! real_fopen) {
   1578 		real_fopen = (FILE* (*)(const char *, const char *))
   1579 			dlsym(RTLD_NEXT, "fopen");
   1580 	}
   1581 
   1582 	if (debug) fprintf(stderr, "FOPEN: %s %s\n", pathname, mode);
   1583 
   1584 	if (strstr(pathname, "xdummy_modified_xconfig.conf")) {
   1585 		/* make our config appear to be in /etc/X11, etc. */
   1586 		char *q = strrchr(pathname, '/');
   1587 		if (q != NULL && getenv("XDUMMY_TMPDIR") != NULL) {
   1588 			strcpy(str1, getenv("XDUMMY_TMPDIR"));
   1589 			strcat(str1, q);
   1590 			if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str1);
   1591 			pathname = str1;
   1592 		}
   1593 	}
   1594 
   1595 	if (root) {
   1596 		return(real_fopen(pathname, mode));
   1597 	}
   1598 
   1599 	str = (char *) pathname;
   1600 	if (strstr(pathname, "/var/log") == pathname) {
   1601 		str = tmpdir_path(pathname);
   1602 		if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str);
   1603 	}
   1604 	return(real_fopen(str, mode));
   1605 }
   1606 
   1607 
   1608 #define RETURN0 if (debug) \
   1609 	{fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return 0;
   1610 #define RETURN1 if (debug) \
   1611 	{fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return -1;
   1612 
   1613 int ioctl(int fd, int req, void *ptr) {
   1614 	static int closed_xf86Info_consoleFd = 0;
   1615 	static int (*real_ioctl)(int, int , void *) = NULL;
   1616 
   1617 	CHECKIT
   1618 	if (! real_ioctl) {
   1619 		real_ioctl = (int (*)(int, int , void *))
   1620 			dlsym(RTLD_NEXT, "open");
   1621 	}
   1622 	if (debug) fprintf(stderr, "IOCTL: %d 0x%x %p\n", fd, req, ptr);
   1623 
   1624 	/* based on xorg-x11-6.8.1-dualhead.patch */
   1625 	if (req == VT_GETMODE) {
   1626 		/* close(xf86Info.consoleFd) */
   1627 		if (0 && ! closed_xf86Info_consoleFd) {
   1628 			/* I think better not to close it... */
   1629 			close(fd);
   1630 			closed_xf86Info_consoleFd = 1;
   1631 		}
   1632 		RETURN0
   1633 	} else if (req == VT_SETMODE) {
   1634 		RETURN0
   1635 	} else if (req == VT_GETSTATE) {
   1636 		RETURN0
   1637 	} else if (req == KDSETMODE) {
   1638 		RETURN0
   1639 	} else if (req == KDSETLED) {
   1640 		RETURN0
   1641 	} else if (req == KDGKBMODE) {
   1642 		RETURN0
   1643 	} else if (req == KDSKBMODE) {
   1644 		RETURN0
   1645 	} else if (req == VT_ACTIVATE) {
   1646 		RETURN0
   1647 	} else if (req == VT_WAITACTIVE) {
   1648 		RETURN0
   1649 	} else if (req == VT_RELDISP) {
   1650 		if (ptr == (void *) 1) {
   1651 			RETURN1
   1652 		} else if (ptr == (void *) VT_ACKACQ) {
   1653 			RETURN0
   1654 		}
   1655 	}
   1656 
   1657 	return(real_ioctl(fd, req, ptr));
   1658 }
   1659 
   1660 typedef void (*sighandler_t)(int);
   1661 #define SIGUSR1       10
   1662 #define SIG_DFL       ((sighandler_t)0)
   1663 
   1664 sighandler_t signal(int signum, sighandler_t handler) {
   1665 	static sighandler_t (*real_signal)(int, sighandler_t) = NULL;
   1666 
   1667 	CHECKIT
   1668 	if (! real_signal) {
   1669 		real_signal = (sighandler_t (*)(int, sighandler_t))
   1670 			dlsym(RTLD_NEXT, "signal");
   1671 	}
   1672 
   1673 	if (debug) fprintf(stderr, "SIGNAL: %d %p\n", signum, handler);
   1674 
   1675 	if (signum == SIGUSR1) {
   1676 		if (debug) fprintf(stderr, "SIGNAL: skip SIGUSR1\n");
   1677 		return SIG_DFL;
   1678 	}
   1679 	
   1680 	return(real_signal(signum, handler));
   1681 }
   1682 
   1683 int close(int fd) {
   1684 	static int (*real_close)(int) = NULL;
   1685 
   1686 	CHECKIT
   1687 	if (! real_close) {
   1688 		real_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
   1689 	}
   1690 
   1691 	if (debug) fprintf(stderr, "CLOSE: %d\n", fd);
   1692 	if (!root) {
   1693 		if (fd < 256) {
   1694 			devs[fd][0] = '\0';
   1695 		}
   1696 	}
   1697 	return(real_close(fd));
   1698 }
   1699 
   1700 struct stat {
   1701 	int foo;
   1702 };
   1703 
   1704 int stat(const char *path, struct stat *buf) {
   1705 	static int (*real_stat)(const char *, struct stat *) = NULL;
   1706 
   1707 	CHECKIT
   1708 	if (! real_stat) {
   1709 		real_stat = (int (*)(const char *, struct stat *))
   1710 			dlsym(RTLD_NEXT, "stat");
   1711 	}
   1712 
   1713 	if (debug) fprintf(stderr, "STAT: %s\n", path);
   1714 
   1715 	return(real_stat(path, buf));
   1716 }
   1717 
   1718 int stat64(const char *path, struct stat *buf) {
   1719 	static int (*real_stat64)(const char *, struct stat *) = NULL;
   1720 
   1721 	CHECKIT
   1722 	if (! real_stat64) {
   1723 		real_stat64 = (int (*)(const char *, struct stat *))
   1724 			dlsym(RTLD_NEXT, "stat64");
   1725 	}
   1726 
   1727 	if (debug) fprintf(stderr, "STAT64: %s\n", path);
   1728 
   1729 	return(real_stat64(path, buf));
   1730 }
   1731 
   1732 int chown(const char *path, uid_t owner, gid_t group) {
   1733 	static int (*real_chown)(const char *, uid_t, gid_t) = NULL;
   1734 
   1735 	CHECKIT
   1736 	if (! real_chown) {
   1737 		real_chown = (int (*)(const char *, uid_t, gid_t))
   1738 			dlsym(RTLD_NEXT, "chown");
   1739 	}
   1740 
   1741 	if (root) {
   1742 		return(real_chown(path, owner, group));
   1743 	}
   1744 
   1745 	if (debug) fprintf(stderr, "CHOWN: %s %d %d\n", path, owner, group);
   1746 
   1747 	if (strstr(path, "/dev") == path) {
   1748 		if (debug) fprintf(stderr, "CHOWN: return 0\n");
   1749 		return 0;
   1750 	}
   1751 
   1752 	return(real_chown(path, owner, group));
   1753 }
   1754 
   1755 extern int *__errno_location (void);
   1756 #ifndef ENODEV
   1757 #define ENODEV 19
   1758 #endif
   1759 
   1760 int ioperm(unsigned long from, unsigned long num, int turn_on) {
   1761 	static int (*real_ioperm)(unsigned long, unsigned long, int) = NULL;
   1762 
   1763 	CHECKIT
   1764 	if (! real_ioperm) {
   1765 		real_ioperm = (int (*)(unsigned long, unsigned long, int))
   1766 			dlsym(RTLD_NEXT, "ioperm");
   1767 	}
   1768 	if (debug) fprintf(stderr, "IOPERM: %d %d %d\n", (int) from, (int) num, turn_on);
   1769 	if (root) {
   1770 		return(real_ioperm(from, num, turn_on));
   1771 	}
   1772 	if (from == 0 && num == 1024 && turn_on == 1) {
   1773 		/* we want xf86EnableIO to fail */
   1774 		if (debug) fprintf(stderr, "IOPERM: setting ENODEV.\n");
   1775 		*__errno_location() = ENODEV;
   1776 		return -1;
   1777 	}
   1778 	return 0;
   1779 }
   1780 
   1781 int iopl(int level) {
   1782 	static int (*real_iopl)(int) = NULL;
   1783 
   1784 	CHECKIT
   1785 	if (! real_iopl) {
   1786 		real_iopl = (int (*)(int)) dlsym(RTLD_NEXT, "iopl");
   1787 	}
   1788 	if (debug) fprintf(stderr, "IOPL: %d\n", level);
   1789 	if (root) {
   1790 		return(real_iopl(level));
   1791 	}
   1792 	return 0;
   1793 }
   1794 
   1795 #ifdef INTERPOSE_GETUID 
   1796 
   1797 /*
   1798  * we got things to work w/o pretending to be root.
   1799  * so we no longer interpose getuid(), etc.
   1800  */
   1801 
   1802 uid_t getuid(void) {
   1803 	static uid_t (*real_getuid)(void) = NULL;
   1804 	CHECKIT
   1805 	if (! real_getuid) {
   1806 		real_getuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid");
   1807 	}
   1808 	if (root) {
   1809 		return(real_getuid());
   1810 	}
   1811 	if (debug) fprintf(stderr, "GETUID: 0\n");
   1812 	return 0;
   1813 }
   1814 uid_t geteuid(void) {
   1815 	static uid_t (*real_geteuid)(void) = NULL;
   1816 	CHECKIT
   1817 	if (! real_geteuid) {
   1818 		real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
   1819 	}
   1820 	if (root) {
   1821 		return(real_geteuid());
   1822 	}
   1823 	if (debug) fprintf(stderr, "GETEUID: 0\n");
   1824 	return 0;
   1825 }
   1826 uid_t geteuid_kludge1(void) {
   1827 	static uid_t (*real_geteuid)(void) = NULL;
   1828 	CHECKIT
   1829 	if (! real_geteuid) {
   1830 		real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
   1831 	}
   1832 	if (debug) fprintf(stderr, "GETEUID: 0 saw_libmodules=%d\n", saw_lib_modules);
   1833 	if (root && !saw_lib_modules) {
   1834 		return(real_geteuid());
   1835 	} else {
   1836 		saw_lib_modules = 0;
   1837 		return 0;
   1838 	}
   1839 }
   1840 
   1841 uid_t getuid32(void) {
   1842 	static uid_t (*real_getuid32)(void) = NULL;
   1843 	CHECKIT
   1844 	if (! real_getuid32) {
   1845 		real_getuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid32");
   1846 	}
   1847 	if (root) {
   1848 		return(real_getuid32());
   1849 	}
   1850 	if (debug) fprintf(stderr, "GETUID32: 0\n");
   1851 	return 0;
   1852 }
   1853 uid_t geteuid32(void) {
   1854 	static uid_t (*real_geteuid32)(void) = NULL;
   1855 	CHECKIT
   1856 	if (! real_geteuid32) {
   1857 		real_geteuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid32");
   1858 	}
   1859 	if (root) {
   1860 		return(real_geteuid32());
   1861 	}
   1862 	if (debug) fprintf(stderr, "GETEUID32: 0\n");
   1863 	return 0;
   1864 }
   1865 
   1866 gid_t getgid(void) {
   1867 	static gid_t (*real_getgid)(void) = NULL;
   1868 	CHECKIT
   1869 	if (! real_getgid) {
   1870 		real_getgid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid");
   1871 	}
   1872 	if (root) {
   1873 		return(real_getgid());
   1874 	}
   1875 	if (debug) fprintf(stderr, "GETGID: 0\n");
   1876 	return 0;
   1877 }
   1878 gid_t getegid(void) {
   1879 	static gid_t (*real_getegid)(void) = NULL;
   1880 	CHECKIT
   1881 	if (! real_getegid) {
   1882 		real_getegid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid");
   1883 	}
   1884 	if (root) {
   1885 		return(real_getegid());
   1886 	}
   1887 	if (debug) fprintf(stderr, "GETEGID: 0\n");
   1888 	return 0;
   1889 }
   1890 gid_t getgid32(void) {
   1891 	static gid_t (*real_getgid32)(void) = NULL;
   1892 	CHECKIT
   1893 	if (! real_getgid32) {
   1894 		real_getgid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid32");
   1895 	}
   1896 	if (root) {
   1897 		return(real_getgid32());
   1898 	}
   1899 	if (debug) fprintf(stderr, "GETGID32: 0\n");
   1900 	return 0;
   1901 }
   1902 gid_t getegid32(void) {
   1903 	static gid_t (*real_getegid32)(void) = NULL;
   1904 	CHECKIT
   1905 	if (! real_getegid32) {
   1906 		real_getegid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid32");
   1907 	}
   1908 	if (root) {
   1909 		return(real_getegid32());
   1910 	}
   1911 	if (debug) fprintf(stderr, "GETEGID32: 0\n");
   1912 	return 0;
   1913 }
   1914 #endif
   1915 
   1916 #if 0
   1917 /* maybe we need to interpose on strcmp someday... here is the template */
   1918 int strcmp(const char *s1, const char *s2) {
   1919 	static int (*real_strcmp)(const char *, const char *) = NULL;
   1920 	CHECKIT
   1921 	if (! real_strcmp) {
   1922 		real_strcmp = (int (*)(const char *, const char *)) dlsym(RTLD_NEXT, "strcmp");
   1923 	}
   1924 	if (debug) fprintf(stderr, "STRCMP: '%s' '%s'\n", s1, s2);
   1925 	return(real_strcmp(s1, s2));
   1926 }
   1927 #endif
   1928 
   1929 #code_end
   1930 }
   1931