]> git.sur5r.net Git - i3/i3/blob - testcases/Xdummy
Merge branch 'tree' into next
[i3/i3] / testcases / Xdummy
1 #!/bin/sh
2 # ----------------------------------------------------------------------
3 #    Copyright (C) 2005-2010 Karl J. Runge <runge@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 }