2 # ----------------------------------------------------------------------
3 # Copyright (C) 2005-2010 Karl J. Runge <runge@karlrunge.com>
6 # This file is part of Xdummy.
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.
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.
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 # ----------------------------------------------------------------------
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.
28 # Run "Xdummy -help" for more info.
45 PATH=$PATH:/bin:/usr/bin
48 program=`basename "$0"`
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".
59 In other words, it tries to make Xorg/XFree86 with the "dummy"
60 device driver act more like Xvfb(1).
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.
68 So, for example, x11vnc+Xdummy terminal services are a little better
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.
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.
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.
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.
92 Your Linux distribution may not install the dummy driver by default,
95 /usr/lib/xorg/modules/drivers/dummy_drv.so
97 some have it in a package named xserver-xorg-video-dummy you that
102 $program <${program}-args> <Xserver-args>
104 (actually, the arguments can be supplied in any order.)
114 $program -tmpdir ~/mytmp :1 -nolisten tcp
118 startx -e bash -- $program :2 -depth 16
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.)
126 xdm -config /usr/local/dummy/xdm-config -nodaemon
128 where the xdm-config file has line:
130 DisplayManager.servers: /usr/local/dummy/Xservers
132 and /usr/local/dummy/Xservers has lines:
134 :1 local /usr/local/dummy/Xdummy :1 -debug
135 :2 local /usr/local/dummy/Xdummy :2 -debug
143 Root permission and x11vnc:
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
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:
153 - add this to sudo via visudo:
155 ALL ALL = NOPASSWD: /usr/local/bin/Xdummy
157 - use this little suid wrapper:
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
169 #include <sys/types.h>
172 int main (int argc, char *argv[]) {
173 extern char **environ;
175 sprintf(str, "XDUMMY_UID=%d", (int) getuid());
179 execv("/usr/local/bin/Xdummy", argv);
189 -install Compile the LD_PRELOAD shared object and install it
190 next to the $program script file as:
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)
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
206 You can also set the CFLAGS environment variable
207 to anything else you want on the compile cmdline.
209 -uninstall Remove the file:
213 The LD_PRELOAD shared object will then be compiled
214 each time this program is run.
216 The X server is not started under -install, -uninstall, or -prconf.
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.
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.
229 The option -geometry can be used instead of -geom;
230 x11vnc calls Xdummy and Xvfb this way.
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.
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.
242 -DEPTH n Same as -depth, except not passed to X server.
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.
249 -nonroot Run in non-root mode (working 12/2009, now default)
251 -root Run as root (may still be needed in some
252 environments.) Same as XDUMMY_RUN_AS_ROOT=1.
254 -nosudo Do not try to use sudo(1) when re-running as root,
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:
262 /usr/X11R6/bin/XFree86
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.
270 -prconf Print, to stdout, the tweaked Xorg/XFree86
271 config file (-config and -xf86config server
272 options, respectively.) The Xserver is not
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.
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
284 -debug Extra debugging output.
286 -strace strace(1) the Xserver process (for troubleshooting.)
287 -ltrace ltrace(1) instead of strace (can be slow.)
289 -h, -help Print out this help.
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
298 :N X Display number for server to use.
300 vtNN Linux virtual terminal (VT) to use (a VT is currently
301 still used, just not switched to and from.)
303 -config file Driver "dummy" tweaked config file, a
304 -xf86config file number of settings are tweaked besides Driver.
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.)
312 Use -config xdummy-builtin to force usage of the builtin config.
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.
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.
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
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.)
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 :-)
337 Set XDUMMY_SET_XV=1 to turn on debugging output for this script.
346 if [ "X$XDUMMY_SET_XV" != "X" ]; then
350 if [ "X$XDUMMY_UID" = "X" ]; then
354 if [ "X$XDUMMY_UID" = "X0" ]; then
355 if [ "X$SUDO_UID" != "X" ]; then
361 # check if root=1 first:
363 if [ "X$XDUMMY_RUN_AS_ROOT" = "X1" ]; then
368 if [ "X$arg" = "X-nonroot" ]; then
370 elif [ "X$arg" = "X-root" ]; then
375 # See if it really needs to be run as root:
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:
380 export XDUMMY_SU_EXEC
387 if [ "X$arg" = "X-nonroot" ]; then
389 elif [ "X$arg" = "X-nosudo" ]; then
391 elif [ "X$arg" = "X-help" ]; then
393 elif [ "X$arg" = "X-h" ]; then
395 elif [ "X$arg" = "X-install" ]; then
397 elif [ "X$arg" = "X-uninstall" ]; then
399 elif [ "X$arg" = "X-n" ]; then
401 elif [ "X$arg" = "X-prconf" ]; then
406 # we need to restart it with su/sudo:
407 if type sudo > /dev/null 2>&1; then
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 "$@"
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 $*"
432 # This will hold the X display, e.g. :20
438 # Process Xdummy args:
440 while [ "X$1" != "X" ]
442 if [ "X$1" = "X-config" -o "X$1" = "X-xf86config" ]; then
448 "-install") install=1; runit=""
450 "-uninstall") uninstall=1; runit=""
458 "-prconf") prconf=1; runit=""
460 "-notweak") notweak=1
470 "-xserver") xserver="$2"; shift
472 "-uid") XDUMMY_UID="$2"; shift
475 "-geom") geom="$2"; shift
477 "-geometry") geom="$2"; shift
479 "-nomodelines") nomodelines=1
481 "-depth") depth="$2"; args="$args -depth $2";
484 "-DEPTH") depth="$2"; shift
486 "-tmpdir") XDUMMY_TMPDIR="$2"; shift
498 "-help") help; exit 0
506 # Try to get a username for use in our tmp directory, etc.
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
513 elif [ "X$LOGNAME" != "X" ]; then
519 if [ "X$user" = "X" ]; then
520 user=`whoami 2>/dev/null`
522 if [ "X$user" = "X" ]; then
523 user=`basename "$HOME"`
525 if [ "X$user" = "X" -o "X$user" = "X." ]; then
529 if [ "X$debug" = "X1" -a "X$runit" != "X" ]; then
532 env | egrep -v '^(LS_COLORS|TERMCAP)' | sort
536 # Function to compile the LD_PRELOAD shared object:
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}'`
545 tmp=$tdir/Xdummy.$RANDOM$$.c
547 if [ -e $tmp -o -h $tmp ]; then
548 warn "$tmp still exists."
552 tail -n +$n1 $0 | head -n $dn > $tmp
554 # compile it to Xdummy.so:
555 if [ -f "$SO" ]; then
561 if [ ! -f "$SO" ]; then
562 SO=$tdir/Xdummy.$user.so
563 warn "warning switching LD_PRELOAD shared object to: $SO"
566 if [ -f "$SO" ]; then
573 if [ "X$INTERPOSE_GETUID" = "X1" ]; then
574 CFLAGS="$CFLAGS -DINTERPOSE_GETUID"
576 echo "$program:" cc -shared -fPIC $CFLAGS -o $SO $tmp
577 cc -shared -fPIC $CFLAGS -o $SO $tmp
580 if [ $rc != 0 ]; then
581 warn "$program: cannot build $SO"
584 if [ "X$debug" != "X" -o "X$install" != "X" ]; then
585 warn "$program: created $SO"
590 # Set tdir to tmp dir for make_so():
591 if [ "X$XDUMMY_TMPDIR" != "X" ]; then
598 # Handle -install/-uninstall case:
600 if [ "X$install" != "X" -o "X$uninstall" != "X" ]; then
601 if [ -e "$SO" -o -h "$SO" ]; then
602 warn "$program: removing $SO"
604 if [ -f "$SO" ]; then
609 if [ -e "$SO" -o -h "$SO" ]; then
610 warn "warning: $SO still exists."
613 if [ $install ]; then
615 if [ ! -f "$SO" ]; then
622 # We need a tmp directory for the .so, tweaked config file, and for
623 # redirecting filenames we cannot create (under -nonroot)
626 if [ "X$XDUMMY_TMPDIR" = "X" ]; then
627 XDUMMY_TMPDIR="/tmp/Xdummy.$user"
629 # try to tack on a unique subdir (display number or pid)
630 # to allow multiple instances
632 if [ "X$disp" != "X" ]; then
637 tack=`echo "$t0" | sed -e 's/^.*://'`
638 if echo "$tack" | grep '^[0-9][0-9]*$' > /dev/null; then
643 if [ "X$tack" != "X" ]; then
644 XDUMMY_TMPDIR="$XDUMMY_TMPDIR/$tack"
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"
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
666 # See if we can write something there:
668 tfile="$XDUMMY_TMPDIR/test.file"
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
680 # Compile the LD_PRELOAD shared object if needed (needs XDUMMY_TMPDIR)
682 if [ ! -f "$SO" ]; then
683 SO="$XDUMMY_TMPDIR/Xdummy.so"
687 # Decide which X server to use:
689 if [ "X$xserver" = "X" ]; then
690 if type Xorg >/dev/null 2>&1; then
692 elif type XFree86 >/dev/null 2>&1; then
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"
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"
708 # See if the binary is suid or not readable under -nonroot mode:
710 if [ "X$BASH_VERSION" != "X" ]; then
711 xserver_path=`type -p $xserver 2>/dev/null`
713 xserver_path=`type $xserver 2>/dev/null | awk '{print $NF}'`
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"
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
730 if [ ! -e $new -o ! -s $new ]; then
733 chmod 700 $new || exit 1
734 if [ ! -r $xserver_path ]; then
736 warn "NEED TO COPY UNREADABLE $xserver_path to $new as root:"
738 ls -l $xserver_path 1>&2
740 warn "This only needs to be done once:"
741 warn " cat $xserver_path > $new"
744 if type sudo > /dev/null 2>&1; then
749 if [ "X$nos" = "X1" ]; then
750 warn "Please supply root passwd to 'su -c'"
751 su -c "cat $xserver_path > $new"
753 warn "Please supply the sudo passwd if asked:"
754 sudo /bin/sh -c "cat $xserver_path > $new"
758 warn "COPYING SETUID $xserver_path to $new"
760 ls -l $xserver_path 1>&2
762 cat $xserver_path > $new
773 warn "Please restart Xdummy now."
776 if [ ! -O $new ]; then
777 warn "file \"$new\" not owned by us!"
787 if [ "X$disp" != "X" ]; then
789 elif [ "X$1" != "X" ]; then
790 if echo "$1" | grep '^:[0-9]' > /dev/null; then
793 elif [ "X$1" = "X:" ]; then
794 # ":" means for us to find one.
798 if [ "X$disp" = "X" -o "X$disp" = "X:" ]; then
799 # try to find an open display port:
801 ports=`netstat -ant | grep LISTEN | awk '{print $4}' | sed -e 's/^.*://'`
805 port=`printf "60%02d" $n`
806 if echo "$ports" | grep "^${port}\$" > /dev/null; then
810 warn "$program: auto-selected DISPLAY $disp"
817 # Work out which vt to use, try to find/guess an open one if necessary.
822 if echo "$arg" | grep '^vt' > /dev/null; then
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
836 if fuser $dev >/dev/null 2>&1; then
840 warn "$program: auto-selected VT $vt => $dev"
847 if [ "X$vt" = "X" ]; then
848 # take a wild guess...
850 warn "$program: selected fallback VT $vt"
856 # Decide flavor of Xserver:
858 stype=`basename "$xserver"`
859 if echo "$stype" | grep -i xfree86 > /dev/null; then
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'`
873 # perl script to tweak the config file... add/delete options, etc.
875 env XDUMMY_GEOM=$geom \
876 XDUMMY_DEPTH=$depth \
877 XDUMMY_NOMODELINES=$nomodelines \
878 perl > $config2 < $in -e '
880 $geom = $ENV{XDUMMY_GEOM};
881 $depth = $ENV{XDUMMY_DEPTH};
882 $nomodelines = $ENV{XDUMMY_NOMODELINES};
885 $HorizSync = "30.0 - 130.0";
886 $VertRefresh = "50.0 - 250.0";
889 foreach $g (split(/,/, $geom)) {
891 if (!$nomodelines && $g =~ /(\d+)x(\d+)/) {
894 $mode_str .= " Modeline \"$g\" ";
895 my $dot = sprintf("%.2f", $w * $h * 70 * 1.e-6);
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);
912 if ($ENV{XDUMMY_NOTWEAK}) {
918 # pass comments straight thru
922 if (/^\s*Section\s+(\S+)/i) {
931 if (/^\s*EndSection/i) {
933 if ($sect eq "serverflags") {
934 if (!$got_DontVTSwitch) {
935 print " ##Xdummy:##\n";
936 print " Option \"DontVTSwitch\" \"true\"\n";
938 if (!$got_AllowMouseOpenFail) {
939 print " ##Xdummy:##\n";
940 print " Option \"AllowMouseOpenFail\" \"true\"\n";
942 if (!$got_PciForceNone) {
943 print " ##Xdummy:##\n";
944 print " Option \"PciForceNone\" \"true\"\n";
946 } elsif ($sect eq "device") {
948 print " ##Xdummy:##\n";
949 print " Driver \"dummy\"\n";
951 if (!$got_VideoRam) {
952 print " ##Xdummy:##\n";
953 print " VideoRam $videoram\n";
955 } elsif ($sect eq "screen") {
956 if ($depth ne "" && !got_DefaultDepth) {
957 print " ##Xdummy:##\n";
958 print " DefaultDepth $depth\n";
960 if ($got_Monitor eq "") {
961 print " ##Xdummy:##\n";
962 print " Monitor \"Monitor0\"\n";
964 } elsif ($sect eq "monitor") {
965 if (!got_HorizSync) {
966 print " ##Xdummy:##\n";
967 print " HorizSync $HorizSync\n";
969 if (!got_VertRefresh) {
970 print " ##Xdummy:##\n";
971 print " VertRefresh $VertRefresh\n";
974 print " ##Xdummy:##\n";
983 if (/^\s*SubSection\s+(\S+)/i) {
987 $subsect =~ y/A-Z/a-z/;
988 $subsects{$subsect} = 1;
989 if ($sect eq "screen" && $subsect eq "display") {
995 if (/^\s*EndSubSection/i) {
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";
1003 if ($geom ne "" && ! $got_Modes) {
1004 print " ##Xdummy:##\n";
1005 print " Modes\t$geom\n";
1016 if ($sect eq "serverflags") {
1017 if ($l =~ /^\s*Option.*DontVTSwitch/i) {
1018 $_ =~ s/false/true/ig;
1019 $got_DontVTSwitch = 1;
1021 if ($l =~ /^\s*Option.*AllowMouseOpenFail/i) {
1022 $_ =~ s/false/true/ig;
1023 $got_AllowMouseOpenFail = 1;
1025 if ($l =~ /^\s*Option.*PciForceNone/i) {
1026 $_ =~ s/false/true/ig;
1027 $got_PciForceNone= 1;
1030 if ($sect eq "module") {
1031 if ($l =~ /^\s*Load.*\b(dri|fbdevhw)\b/i) {
1032 $_ = "##Xdummy## $_";
1035 if ($sect eq "monitor") {
1036 if ($l =~ /^\s*HorizSync/i) {
1039 if ($l =~ /^\s*VertRefresh/i) {
1040 $got_VertRefresh = 1;
1043 if ($sect eq "device") {
1044 if ($l =~ /^(\s*Driver)\b/i) {
1045 $_ = "$1 \"dummy\"\n";
1048 if ($l =~ /^\s*VideoRam/i) {
1052 if ($sect eq "inputdevice") {
1053 if ($l =~ /^\s*Option.*\bDevice\b/i) {
1054 print " ##Xdummy:##\n";
1055 $_ = " Option \"Device\" \"/dev/dilbert$n\"\n";
1058 if ($sect eq "screen") {
1059 if ($l =~ /^\s*DefaultDepth\s+(\d+)/i) {
1061 print " ##Xdummy:##\n";
1062 $_ = " DefaultDepth\t$depth\n";
1064 $got_DefaultDepth = 1;
1066 if ($l =~ /^\s*Monitor\s+(\S+)/i) {
1068 $got_Monitor =~ s/"//g;
1070 if ($subsect eq "display") {
1072 if ($l =~ /^(\s*Modes)\b/i) {
1073 print " ##Xdummy:##\n";
1078 if ($l =~ /^\s*Depth\s+(\d+)/i) {
1080 if (!$set_Depth && $depth ne "") {
1083 print " ##Xdummy:##\n";
1084 $_ = " Depth\t$depth\n";
1092 if ($ENV{XDUMMY_NOTWEAK}) {
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";
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";
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";
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";
1128 print " Monitor \"Monitor0\"\n";
1131 print " DefaultDepth $depth\n";
1133 print " DefaultDepth 24\n";
1135 print " SubSection \"Display\"\n";
1136 print " Viewport 0 0\n";
1137 print " Depth 24\n";
1140 } elsif ($geom ne "") {
1141 print " Modes $geom\n";
1143 print " Modes \"1280x1024\" \"1024x768\" \"800x600\"\n";
1145 print " EndSubSection\n";
1146 print "EndSection\n";
1151 # Work out config file and tweak it.
1153 if [ "X$cmdline_config" = "X" ]; then
1155 elif [ "X$cmdline_config" = "Xxdummy-builtin" ]; then
1157 elif echo "$cmdline_config" | grep '/' > /dev/null; then
1160 # ignore basename only case (let server handle it)
1165 config=$cmdline_config
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.
1170 export XDUMMY_NOTWEAK
1174 if [ ! $notweak ]; then
1175 # tweaked config will be put in $config2:
1177 if [ "X$config" = "X" ]; then
1178 # use the default one:
1179 if [ "X$stype" = "Xxorg" ]; then
1180 config=/etc/X11/xorg.conf
1182 if [ -f "/etc/X11/XF86Config-4" ]; then
1183 config="/etc/X11/XF86Config-4"
1185 config="/etc/X11/XF86Config"
1188 if [ ! -f "$config" ]; then
1189 for c in /etc/X11/xorg.conf /etc/X11/XF86Config-4 /etc/X11/XF86Config
1199 if [ "X$config" = "Xxdummy-builtin" ]; then
1203 if [ ! -f "$config" ]; then
1204 config="$XDUMMY_TMPDIR/xorg.conf"
1205 warn "$program: using minimal built-in xorg.conf settings."
1208 Section "ServerLayout"
1209 Identifier "Layout0"
1211 InputDevice "Keyboard0" "CoreKeyboard"
1212 InputDevice "Mouse0" "CorePointer"
1225 Section "InputDevice"
1228 Option "Protocol" "auto"
1229 Option "Device" "/dev/psaux"
1230 Option "Emulate3Buttons" "no"
1231 Option "ZAxisMapping" "4 5"
1234 Section "InputDevice"
1235 Identifier "Keyboard0"
1240 Identifier "Monitor0"
1241 VendorName "Unknown"
1243 HorizSync 30.0 - 130.0
1244 VertRefresh 50.0 - 250.0
1249 Identifier "Device0"
1251 VendorName "foovideo Corporation"
1255 Identifier "Screen0"
1259 SubSection "Display"
1268 if [ -f "$config" ]; then
1269 tweak_config $config
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"
1278 args="$args -xf86config FUBAR"
1281 if [ "X$config2" != "X" ]; then
1284 if [ "X$root" = "X" ]; then
1285 # ordinary user cannot use absolute path.
1286 c2=`basename $config2`
1288 args=`echo "$args" | sed \
1289 -e "s,-config *[^ ][^ ]*,-config $c2,g" \
1290 -e "s,-xf86config *[^ ][^ ]*,-xf86config $c2,g"`
1294 if [ $prconf ]; then
1296 warn "Printing out the Xorg/XFree86 server config file:"
1298 if [ "X$config2" = "X" ]; then
1299 warn "NO CONFIG GENERATED."
1318 if [ "X$debug" != "X" -o "X$runit" = "X" ]; then
1319 if [ ! $runit ]; then
1321 echo "/usr/bin/env:"
1322 env | egrep -v '^(LS_COLORS|TERMCAP)' | sort
1325 env | grep '^XDUMMY' | sort
1329 warn "The command to run is:"
1333 if echo "$so" | grep '^\./' > /dev/null; then
1334 so=`echo "$so" | sed -e "s,^\.,$pwd,"`
1336 if echo "$so" | grep '/' > /dev/null; then
1341 warn "env LD_PRELOAD=$so $xserver $disp $args $vt"
1343 if [ ! $runit ]; then
1348 if [ $strace ]; then
1349 if [ "X$strace" = "X2" ]; then
1350 ltrace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
1352 strace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
1355 exec env LD_PRELOAD=$SO $xserver $disp $args $vt
1360 #########################################################################
1365 #define O_ACCMODE 0003
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
1384 #include <linux/vt.h>
1385 #include <linux/kd.h>
1390 static char tmpdir[4096];
1391 static char str1[4096];
1392 static char str2[4096];
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;
1401 static time_t start = 0;
1403 void check_debug(void) {
1405 if (getenv("XDUMMY_DEBUG") != NULL) {
1410 /* prevent other processes using the preload: */
1411 putenv("LD_PRELOAD=");
1414 void check_root(void) {
1416 /* script tells us if we are root */
1417 if (getenv("XDUMMY_ROOT") != NULL) {
1425 void check_uid(void) {
1428 if (debug) fprintf(stderr, "START: %u\n", (unsigned int) start);
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);
1436 /* this will simply fail in -nonroot mode: */
1445 #define CHECKIT if (debug < 0) check_debug(); \
1446 if (root < 0) check_root(); \
1449 static void set_tmpdir(void) {
1451 static int didset = 0;
1455 s = getenv("XDUMMY_TMPDIR");
1461 strcat(tmpdir, "/");
1465 static char *tmpdir_path(const char *path) {
1476 strcpy(str1, tmpdir);
1481 int open(const char *pathname, int flags, unsigned short mode) {
1483 char *store_dev = NULL;
1484 static int (*real_open)(const char *, int , unsigned short) = NULL;
1488 real_open = (int (*)(const char *, int , unsigned short))
1489 dlsym(RTLD_NEXT, "open");
1492 if (strstr(pathname, "lib/modules/")) {
1493 /* not currently used. */
1494 saw_lib_modules = 1;
1498 if (strstr(pathname, "/dev/") == pathname) {
1499 store_dev = strdup(pathname);
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 */
1506 mkfifo(pathname, 0666);
1508 /* we used to handle more /dev files ... */
1509 fd = real_open(pathname, O_WRONLY|O_CREAT, 0777);
1514 fd = real_open(pathname, flags, mode);
1516 if (debug) fprintf(stderr, "OPEN: %s %d %d fd=%d\n", pathname, flags, mode, fd);
1521 strcpy(devs[fd], store_dev);
1530 int open64(const char *pathname, int flags, unsigned short mode) {
1534 if (debug) fprintf(stderr, "OPEN64: %s %d %d\n", pathname, flags, mode);
1536 fd = open(pathname, flags, mode);
1540 int rename(const char *oldpath, const char *newpath) {
1541 static int (*real_rename)(const char *, const char *) = NULL;
1544 if (! real_rename) {
1545 real_rename = (int (*)(const char *, const char *))
1546 dlsym(RTLD_NEXT, "rename");
1549 if (debug) fprintf(stderr, "RENAME: %s %s\n", oldpath, newpath);
1552 return(real_rename(oldpath, newpath));
1555 if (strstr(oldpath, "/var/log") == oldpath) {
1556 if (debug) fprintf(stderr, "RENAME: returning 0\n");
1559 return(real_rename(oldpath, newpath));
1562 FILE *fopen(const char *pathname, const char *mode) {
1563 static FILE* (*real_fopen)(const char *, const char *) = NULL;
1567 if (strstr(pathname, "/fonts/")) {
1568 if (strstr(pathname, "fonts.dir")) {
1570 } else if (strstr(pathname, "fonts.alias")) {
1578 real_fopen = (FILE* (*)(const char *, const char *))
1579 dlsym(RTLD_NEXT, "fopen");
1582 if (debug) fprintf(stderr, "FOPEN: %s %s\n", pathname, mode);
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"));
1590 if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str1);
1596 return(real_fopen(pathname, mode));
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);
1604 return(real_fopen(str, mode));
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;
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;
1619 real_ioctl = (int (*)(int, int , void *))
1620 dlsym(RTLD_NEXT, "open");
1622 if (debug) fprintf(stderr, "IOCTL: %d 0x%x %p\n", fd, req, ptr);
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... */
1630 closed_xf86Info_consoleFd = 1;
1633 } else if (req == VT_SETMODE) {
1635 } else if (req == VT_GETSTATE) {
1637 } else if (req == KDSETMODE) {
1639 } else if (req == KDSETLED) {
1641 } else if (req == KDGKBMODE) {
1643 } else if (req == KDSKBMODE) {
1645 } else if (req == VT_ACTIVATE) {
1647 } else if (req == VT_WAITACTIVE) {
1649 } else if (req == VT_RELDISP) {
1650 if (ptr == (void *) 1) {
1652 } else if (ptr == (void *) VT_ACKACQ) {
1657 return(real_ioctl(fd, req, ptr));
1660 typedef void (*sighandler_t)(int);
1662 #define SIG_DFL ((sighandler_t)0)
1664 sighandler_t signal(int signum, sighandler_t handler) {
1665 static sighandler_t (*real_signal)(int, sighandler_t) = NULL;
1668 if (! real_signal) {
1669 real_signal = (sighandler_t (*)(int, sighandler_t))
1670 dlsym(RTLD_NEXT, "signal");
1673 if (debug) fprintf(stderr, "SIGNAL: %d %p\n", signum, handler);
1675 if (signum == SIGUSR1) {
1676 if (debug) fprintf(stderr, "SIGNAL: skip SIGUSR1\n");
1680 return(real_signal(signum, handler));
1684 static int (*real_close)(int) = NULL;
1688 real_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
1691 if (debug) fprintf(stderr, "CLOSE: %d\n", fd);
1697 return(real_close(fd));
1704 int stat(const char *path, struct stat *buf) {
1705 static int (*real_stat)(const char *, struct stat *) = NULL;
1709 real_stat = (int (*)(const char *, struct stat *))
1710 dlsym(RTLD_NEXT, "stat");
1713 if (debug) fprintf(stderr, "STAT: %s\n", path);
1715 return(real_stat(path, buf));
1718 int stat64(const char *path, struct stat *buf) {
1719 static int (*real_stat64)(const char *, struct stat *) = NULL;
1722 if (! real_stat64) {
1723 real_stat64 = (int (*)(const char *, struct stat *))
1724 dlsym(RTLD_NEXT, "stat64");
1727 if (debug) fprintf(stderr, "STAT64: %s\n", path);
1729 return(real_stat64(path, buf));
1732 int chown(const char *path, uid_t owner, gid_t group) {
1733 static int (*real_chown)(const char *, uid_t, gid_t) = NULL;
1737 real_chown = (int (*)(const char *, uid_t, gid_t))
1738 dlsym(RTLD_NEXT, "chown");
1742 return(real_chown(path, owner, group));
1745 if (debug) fprintf(stderr, "CHOWN: %s %d %d\n", path, owner, group);
1747 if (strstr(path, "/dev") == path) {
1748 if (debug) fprintf(stderr, "CHOWN: return 0\n");
1752 return(real_chown(path, owner, group));
1755 extern int *__errno_location (void);
1760 int ioperm(unsigned long from, unsigned long num, int turn_on) {
1761 static int (*real_ioperm)(unsigned long, unsigned long, int) = NULL;
1764 if (! real_ioperm) {
1765 real_ioperm = (int (*)(unsigned long, unsigned long, int))
1766 dlsym(RTLD_NEXT, "ioperm");
1768 if (debug) fprintf(stderr, "IOPERM: %d %d %d\n", (int) from, (int) num, turn_on);
1770 return(real_ioperm(from, num, turn_on));
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;
1781 int iopl(int level) {
1782 static int (*real_iopl)(int) = NULL;
1786 real_iopl = (int (*)(int)) dlsym(RTLD_NEXT, "iopl");
1788 if (debug) fprintf(stderr, "IOPL: %d\n", level);
1790 return(real_iopl(level));
1795 #ifdef INTERPOSE_GETUID
1798 * we got things to work w/o pretending to be root.
1799 * so we no longer interpose getuid(), etc.
1802 uid_t getuid(void) {
1803 static uid_t (*real_getuid)(void) = NULL;
1805 if (! real_getuid) {
1806 real_getuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid");
1809 return(real_getuid());
1811 if (debug) fprintf(stderr, "GETUID: 0\n");
1814 uid_t geteuid(void) {
1815 static uid_t (*real_geteuid)(void) = NULL;
1817 if (! real_geteuid) {
1818 real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
1821 return(real_geteuid());
1823 if (debug) fprintf(stderr, "GETEUID: 0\n");
1826 uid_t geteuid_kludge1(void) {
1827 static uid_t (*real_geteuid)(void) = NULL;
1829 if (! real_geteuid) {
1830 real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
1832 if (debug) fprintf(stderr, "GETEUID: 0 saw_libmodules=%d\n", saw_lib_modules);
1833 if (root && !saw_lib_modules) {
1834 return(real_geteuid());
1836 saw_lib_modules = 0;
1841 uid_t getuid32(void) {
1842 static uid_t (*real_getuid32)(void) = NULL;
1844 if (! real_getuid32) {
1845 real_getuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid32");
1848 return(real_getuid32());
1850 if (debug) fprintf(stderr, "GETUID32: 0\n");
1853 uid_t geteuid32(void) {
1854 static uid_t (*real_geteuid32)(void) = NULL;
1856 if (! real_geteuid32) {
1857 real_geteuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid32");
1860 return(real_geteuid32());
1862 if (debug) fprintf(stderr, "GETEUID32: 0\n");
1866 gid_t getgid(void) {
1867 static gid_t (*real_getgid)(void) = NULL;
1869 if (! real_getgid) {
1870 real_getgid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid");
1873 return(real_getgid());
1875 if (debug) fprintf(stderr, "GETGID: 0\n");
1878 gid_t getegid(void) {
1879 static gid_t (*real_getegid)(void) = NULL;
1881 if (! real_getegid) {
1882 real_getegid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid");
1885 return(real_getegid());
1887 if (debug) fprintf(stderr, "GETEGID: 0\n");
1890 gid_t getgid32(void) {
1891 static gid_t (*real_getgid32)(void) = NULL;
1893 if (! real_getgid32) {
1894 real_getgid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid32");
1897 return(real_getgid32());
1899 if (debug) fprintf(stderr, "GETGID32: 0\n");
1902 gid_t getegid32(void) {
1903 static gid_t (*real_getegid32)(void) = NULL;
1905 if (! real_getegid32) {
1906 real_getegid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid32");
1909 return(real_getegid32());
1911 if (debug) fprintf(stderr, "GETEGID32: 0\n");
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;
1921 if (! real_strcmp) {
1922 real_strcmp = (int (*)(const char *, const char *)) dlsym(RTLD_NEXT, "strcmp");
1924 if (debug) fprintf(stderr, "STRCMP: '%s' '%s'\n", s1, s2);
1925 return(real_strcmp(s1, s2));