From fabb8973111bbcb0c3d698a19b1ddf975eb5cb7c Mon Sep 17 00:00:00 2001 From: Greg Brockman Date: Wed, 10 Aug 2016 16:12:51 -0700 Subject: [PATCH] Switch to Docker for tests (#285) --- .dockerignore | 1 + .travis.yml | 51 +- Dockerfile | 24 +- Makefile | 8 +- bin/docker_entrypoint | 24 +- test.dockerfile | 42 + tox.ini | 30 +- vendor/Xdummy | 1955 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 2076 insertions(+), 59 deletions(-) create mode 100644 .dockerignore create mode 100644 test.dockerfile create mode 100755 vendor/Xdummy diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..172bf5786d1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.tox diff --git a/.travis.yml b/.travis.yml index 12aa6c59a33..095d4c60fba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,50 +1,15 @@ -language: python -python: - - "2.7" - - "3.4" -dist: trusty sudo: required -cache: - apt: true - pip: true -addons: - apt: - sources: - - george-edison55-precise-backports # cmake 3.2.3 - - deadsnakes - packages: - - cmake - - cmake-data - - python-dev - - python3.4-dev - - swig - - libav-tools - - python-numpy - - libboost-all-dev - - libsdl2-dev - - libjpeg-dev - - zlib1g-dev - - libblas-dev - - liblapack-dev - - gfortran - - python-opengl +language: python +services: + - docker before_install: - # Start an X server so rendering works. - - Xvfb :12 -screen 0 800x600x24 +extension RANDR & - + # Prime the cache. We currently manually keep this synced. + - docker pull quay.io/openai/gym:test + - docker build -f test.dockerfile -t quay.io/openai/gym:test . +script: # In a pull request, there are no secrets, and hence no MuJoCo: # https://docs.travis-ci.com/user/pull-requests#Security-Restrictions-when-testing-Pull-Requests. - - '[ -z ${MUJOCO_KEY_BUNDLE+x} ] || ( mkdir -p ~/.mujoco && curl https://openai-public.s3-us-west-2.amazonaws.com/mujoco/$MUJOCO_KEY_BUNDLE.tar.gz | tar xz -C ~/.mujoco )' - - # Without this line, Travis has fork()s fail with an out of memory - # error. (These fork()s are for spawning the subprocess for video - # recording.) We should debug the memory usage at some stage, but - # simply setting overcommit is a good starting point. - - sudo sysctl -w vm.overcommit_memory=1 -env: - - DISPLAY=:12 -install: travis_wait pip install -r requirements_dev.txt -script: nose2 + - docker run -e MUJOCO_KEY_BUNDLE="${MUJOCO_KEY_BUNDLE:-}" quay.io/openai/gym:test tox notifications: slack: diff --git a/Dockerfile b/Dockerfile index b68ec22ccd5..4a16b6787bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,7 @@ FROM ubuntu:14.04 RUN apt-get update \ - && apt-get install -y xorg-dev \ - libgl1-mesa-dev \ - xvfb \ - libxinerama1 \ - libxcursor1 \ - libglu1-mesa \ - libav-tools \ + && apt-get install -y libav-tools \ python-numpy \ python-scipy \ python-pyglet \ @@ -17,16 +11,24 @@ RUN apt-get update \ libjpeg-dev \ curl \ cmake \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && easy_install pip + swig \ + python-opengl \ + libboost-all-dev \ + libsdl2-dev \ + wget \ + unzip \ + git \ + xpra \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && easy_install pip WORKDIR /usr/local/gym RUN mkdir gym && touch gym/__init__.py COPY ./gym/version.py ./gym COPY ./requirements.txt . COPY ./setup.py . -RUN pip install -r requirements.txt +RUN pip install -e .[all] # Finally, upload our actual code! COPY . /usr/local/gym diff --git a/Makefile b/Makefile index 40f423fc0e3..24ffbd93e2b 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,15 @@ install: pip install -r requirements.txt test: - nose2 + docker build -f test.dockerfile -t quay.io/openai/gym:test . upload: rm -rf dist python setup.py sdist twine upload dist/* + +docker-build: + docker build -t quay.io/openai/gym . + +docker-run: + docker run -ti quay.io/openai/gym bash diff --git a/bin/docker_entrypoint b/bin/docker_entrypoint index ab3643ba057..c36a63d7aa8 100755 --- a/bin/docker_entrypoint +++ b/bin/docker_entrypoint @@ -4,9 +4,29 @@ set -e -# Set up display; otherwise rendering will cause segfaults +path=$(cd $(dirname "$0") && pwd) + +[ -z ${MUJOCO_KEY_BUNDLE+x} ] || ( mkdir -p ~/.mujoco && curl https://openai-public.s3-us-west-2.amazonaws.com/mujoco/$MUJOCO_KEY_BUNDLE.tar.gz | tar xz -C ~/.mujoco ) + +# Set up display; otherwise rendering will fail rm -f /tmp/.X12-lock -Xvfb :12 -screen 0 800x600x24 +extension RANDR 2>/dev/null & +"$path/../vendor/Xdummy" :12 & export DISPLAY=:12 +# Wait for the file to come up +display=12 +file="/tmp/.X11-unix/X$display" +for i in $(seq 1 10); do + if [ -e "$file" ]; then + break + fi + + echo "Waiting for $file to be created (try $i/10)" + sleep "$i" +done +if ! [ -e "$file" ]; then + echo "Timing out: $file was not created" + exit 1 +fi + exec "$@" diff --git a/test.dockerfile b/test.dockerfile new file mode 100644 index 00000000000..bd110c0df76 --- /dev/null +++ b/test.dockerfile @@ -0,0 +1,42 @@ +# A Dockerfile that sets up a full Gym install +FROM ubuntu:14.04 + +RUN apt-get update \ + && apt-get install -y libav-tools \ + python-numpy \ + python-scipy \ + python-pyglet \ + python-setuptools \ + libpq-dev \ + libjpeg-dev \ + curl \ + cmake \ + swig \ + python-opengl \ + libboost-all-dev \ + libsdl2-dev \ + wget \ + unzip \ + git \ + xpra \ + python3-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && easy_install pip + +WORKDIR /usr/local/gym +RUN mkdir gym && touch gym/__init__.py +COPY ./gym/version.py ./gym +COPY ./requirements.txt . +COPY ./setup.py . +COPY ./tox.ini . + +RUN pip install tox +# Install the relevant dependencies. Keep printing so Travis knows we're alive. +RUN ["bash", "-c", "( while true; do echo '.'; sleep 60; done ) & tox --notest"] + +# Finally, upload our actual code! +COPY . /usr/local/gym + +ENTRYPOINT ["/usr/local/gym/bin/docker_entrypoint"] +CMD ["tox"] diff --git a/tox.ini b/tox.ini index f9ebd7f2807..8ee05fc3782 100644 --- a/tox.ini +++ b/tox.ini @@ -12,8 +12,21 @@ passenv=DISPLAY TRAVIS* deps = nose2 mock + atari_py>=0.0.17 + Pillow + PyOpenGL + pachi-py>=0.0.19 + box2d-py + PyOpenGL + doom_py>=0.0.11 + mujoco_py>=0.4.3 + keras + theano + numpy>=1.10.4 + requests>=2.0 + six + pyglet>=1.2.0 commands = - pip install -e .[all] nose2 {posargs} [testenv:py27] @@ -22,6 +35,19 @@ passenv=DISPLAY TRAVIS* deps = nose2 mock + atari_py>=0.0.17 + Pillow + PyOpenGL + pachi-py>=0.0.19 + box2d-py + PyOpenGL + doom_py>=0.0.11 + mujoco_py>=0.4.3 + keras + theano + numpy>=1.10.4 + requests>=2.0 + six + pyglet>=1.2.0 commands = - pip install -e .[all] nose2 {posargs} diff --git a/vendor/Xdummy b/vendor/Xdummy new file mode 100755 index 00000000000..ddf54216649 --- /dev/null +++ b/vendor/Xdummy @@ -0,0 +1,1955 @@ +#!/bin/sh +# ---------------------------------------------------------------------- +# Copyright (C) 2005-2011 Karl J. Runge +# All rights reserved. +# +# This file is part of Xdummy. +# +# Xdummy is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at +# your option) any later version. +# +# Xdummy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Xdummy; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA +# or see . +# ---------------------------------------------------------------------- +# +# +# Xdummy: an LD_PRELOAD hack to run a stock Xorg(1) or XFree86(1) server +# with the "dummy" video driver to make it avoid Linux VT switching, etc. +# +# Run "Xdummy -help" for more info. +# +install="" +uninstall="" +runit=1 +prconf="" +notweak="" +root="" +nosudo="" +xserver="" +geom="" +nomodelines="" +depth="" +debug="" +strace="" +cmdline_config="" + +PATH=$PATH:/bin:/usr/bin +export PATH + +program=`basename "$0"` + +help () { + ${PAGER:-more} << END +$program: + + A hack to run a stock Xorg(1) or XFree86(1) X server with the "dummy" + (RAM-only framebuffer) video driver such that it AVOIDS the Linux VT + switching, opening device files in /dev, keyboard and mouse conflicts, + and other problems associated with the normal use of "dummy". + + In other words, it tries to make Xorg/XFree86 with the "dummy" + device driver act more like Xvfb(1). + + The primary motivation for the Xdummy script is to provide a virtual X + server for x11vnc but with more features than Xvfb (or Xvnc); however + it could be used for other reasons (e.g. better automated testing + than with Xvfb.) One nice thing is the dummy server supports RANDR + dynamic resizing while Xvfb does not. + + So, for example, x11vnc+Xdummy terminal services are a little better + than x11vnc+Xvfb. + + To achieve this, while running the real Xserver $program intercepts + system and library calls via the LD_PRELOAD method and modifies + the behavior to make it work correctly (e.g. avoid the VT stuff.) + LD_PRELOAD tricks are usually "clever hacks" and so might not work + in all situations or break when something changes. + + WARNING: Take care in using Xdummy, although it never has it is + possible that it could damage hardware. One can use the -prconf + option to have it print out the xorg.conf config that it would use + and then inspect it carefully before actually using it. + + This program no longer needs to be run as root as of 12/2009. + However, if there are problems for certain situations (usually older + servers) it may perform better if run as root (use the -root option.) + When running as root remember the previous paragraph and that Xdummy + comes without any warranty. + + gcc/cc and other build tools are required for this script to be able + to compile the LD_PRELOAD shared object. Be sure they are installed + on the system. See -install and -uninstall described below. + + Your Linux distribution may not install the dummy driver by default, + e.g: + + /usr/lib/xorg/modules/drivers/dummy_drv.so + + some have it in a package named xserver-xorg-video-dummy you that + need to install. + +Usage: + + $program <${program}-args> + + (actually, the arguments can be supplied in any order.) + +Examples: + + $program -install + + $program :1 + + $program -debug :1 + + $program -tmpdir ~/mytmp :1 -nolisten tcp + +startx example: + + startx -e bash -- $program :2 -depth 16 + + (if startx needs to be run as root, you can su(1) to a normal + user in the bash shell and then launch ~/.xinitrc or ~/.xsession, + gnome-session, startkde, startxfce4, etc.) + +xdm example: + + xdm -config /usr/local/dummy/xdm-config -nodaemon + + where the xdm-config file has line: + + DisplayManager.servers: /usr/local/dummy/Xservers + + and /usr/local/dummy/Xservers has lines: + + :1 local /usr/local/dummy/Xdummy :1 -debug + :2 local /usr/local/dummy/Xdummy :2 -debug + + (-debug is optional) + +gdm/kdm example: + + TBD. + +Config file: + + If the file $program.cfg exists it will be sourced as shell + commands. Usually one will set some variables this way. + To disable sourcing, supply -nocfg or set XDUMMY_NOCFG=1. + +Root permission and x11vnc: + + Update: as of 12/2009 this program no longer must be run as root. + So try it as non-root before running it as root and/or the + following schemes. + + In some circumstances X server program may need to be run as root. + If so, one could run x11vnc as root with -unixpw (it switches + to the user that logs in) and that may be OK, some other ideas: + + - add this to sudo via visudo: + + ALL ALL = NOPASSWD: /usr/local/bin/Xdummy + + - use this little suid wrapper: +/* + * xdummy.c + * + cc -o ./xdummy xdummy.c + sudo cp ./xdummy /usr/local/bin/xdummy + sudo chown root:root /usr/local/bin/xdummy + sudo chmod u+s /usr/local/bin/xdummy + * + */ +#include +#include +#include +#include + +int main (int argc, char *argv[]) { + extern char **environ; + char str[100]; + sprintf(str, "XDUMMY_UID=%d", (int) getuid()); + putenv(str); + setuid(0); + setgid(0); + execv("/usr/local/bin/Xdummy", argv); + exit(1); + return 1; +} + + +Options: + + ${program}-args: + + -install Compile the LD_PRELOAD shared object and install it + next to the $program script file as: + + $0.so + + When that file exists it is used as the LD_PRELOAD + shared object without recompiling. Otherwise, + each time $program is run the LD_PRELOAD shared + object is compiled as a file in /tmp (or -tmpdir) + + If you set the environment variable + INTERPOSE_GETUID=1 when building, then when + $program is run as an ordinary user, the shared + object will interpose getuid() calls and pretend + to be root. Otherwise it doesn't pretend to + be root. + + You can also set the CFLAGS environment variable + to anything else you want on the compile cmdline. + + -uninstall Remove the file: + + $0.so + + The LD_PRELOAD shared object will then be compiled + each time this program is run. + + The X server is not started under -install, -uninstall, or -prconf. + + + :N The DISPLAY (e.g. :15) is often the first + argument. It is passed to the real X server and + also used by the Xdummy script as an identifier. + + -geom geom1[,geom2...] Take the geometry (e.g. 1024x768) or list + of geometries and insert them into the Screen + section of the tweaked X server config file. + Use this to have a different geometry than the + one(s) in the system config file. + + The option -geometry can be used instead of -geom; + x11vnc calls Xdummy and Xvfb this way. + + -nomodelines When you specify -geom/-geometry, $program will + create Modelines for each geometry and put them + in the Monitor section. If you do not want this + then supply -nomodelines. + + -depth n Use pixel color depth n (e.g. 8, 16, or 24). This + makes sure the X config file has a Screen.Display + subsection of this depth. Note this option is + ALSO passed to the X server. + + -DEPTH n Same as -depth, except not passed to X server. + + -tmpdir dir Specify a temporary directory, owned by you and + only writable by you. This is used in place of + /tmp/Xdummy.\$USER/.. to place the $program.so + shared object, tweaked config files, etc. + + -nonroot Run in non-root mode (working 12/2009, now default) + + -root Run as root (may still be needed in some + environments.) Same as XDUMMY_RUN_AS_ROOT=1. + + -nosudo Do not try to use sudo(1) when re-running as root, + use su(1) instead. + + -xserver path Specify the path to the Xserver to use. Default + is to try "Xorg" first and then "XFree86". If + those are not in \$PATH, it tries these locations: + /usr/bin/Xorg + /usr/X11R6/bin/Xorg + /usr/X11R6/bin/XFree86 + + -n Do not run the command to start the X server, + just show the command that $program would run. + The LD_PRELOAD shared object will be built, + if needed. Also note any XDUMMY* environment + variables that need to be set. + + -prconf Print, to stdout, the tweaked Xorg/XFree86 + config file (-config and -xf86config server + options, respectively.) The Xserver is not + started. + + -notweak Do not tweak (modify) the Xorg/XFree86 config file + (system or server command line) at all. The -geom + and similar config file modifications are ignored. + + It is up to you to make sure it is a working + config file (e.g. "dummy" driver, etc.) + Perhaps you want to use a file based on the + -prconf output. + + -nocfg Do not try to source $program.cfg even if it + exists. Same as setting XDUMMY_NOCFG=1. + + -debug Extra debugging output. + + -strace strace(1) the Xserver process (for troubleshooting.) + -ltrace ltrace(1) instead of strace (can be slow.) + + -h, -help Print out this help. + + + Xserver-args: + + Most of the Xorg and XFree86 options will work and are simply + passed along if you supply them. Important ones that may be + supplied if missing: + + :N X Display number for server to use. + + vtNN Linux virtual terminal (VT) to use (a VT is currently + still used, just not switched to and from.) + + -config file Driver "dummy" tweaked config file, a + -xf86config file number of settings are tweaked besides Driver. + + If -config/-xf86config is not given, the system one + (e.g. /etc/X11/xorg.conf) is used. If the system one cannot be + found, a built-in one is used. Any settings in the config file + that are not consistent with "dummy" mode will be overwritten + (unless -notweak is specified.) + + Use -config xdummy-builtin to force usage of the builtin config. + + If "file" is only a basename (e.g. "xorg.dummy.conf") with no /'s, + then no tweaking of it is done: the X server will look for that + basename via its normal search algorithm. If the found file does + not refer to the "dummy" driver, etc, then the X server will fail. + + You can set the env. var. XDUMMY_EXTRA_SERVER_ARGS to hold some + extra Xserver-args too. (Useful for cfg file.) + +Notes: + + The Xorg/XFree86 "dummy" driver is currently undocumented. It works + well in this mode, but it is evidently not intended for end-users. + So it could be removed or broken at any time. + + If the display Xserver-arg (e.g. :1) is not given, or ":" is given + that indicates $program should try to find a free one (based on + tcp ports.) + + If the display virtual terminal, VT, (e.g. vt9) is not given that + indicates $program should try to find a free one (or guess a high one.) + + This program is not completely secure WRT files in /tmp (but it tries + to a good degree.) Better is to use the -tmpdir option to supply a + directory only writable by you. Even better is to get rid of users + on the local machine you do not trust :-) + + Set XDUMMY_SET_XV=1 to turn on debugging output for this script. + +END +} + +warn() { + echo "$*" 1>&2 +} + +if [ "X$XDUMMY_SET_XV" != "X" ]; then + set -xv +fi + +if [ "X$XDUMMY_UID" = "X" ]; then + XDUMMY_UID=`id -u` + export XDUMMY_UID +fi +if [ "X$XDUMMY_UID" = "X0" ]; then + if [ "X$SUDO_UID" != "X" ]; then + XDUMMY_UID=$SUDO_UID + export XDUMMY_UID + fi +fi + +# check if root=1 first: +# +if [ "X$XDUMMY_RUN_AS_ROOT" = "X1" ]; then + root=1 +fi +for arg in $* +do + if [ "X$arg" = "X-nonroot" ]; then + root="" + elif [ "X$arg" = "X-root" ]; then + root=1 + elif [ "X$arg" = "X-nocfg" ]; then + XDUMMY_NOCFG=1 + export XDUMMY_NOCFG + fi +done + +if [ "X$XDUMMY_NOCFG" = "X" -a -f "$0.cfg" ]; then + . "$0.cfg" +fi + +# See if it really needs to be run as root: +# +if [ "X$XDUMMY_SU_EXEC" = "X" -a "X$root" = "X1" -a "X`id -u`" != "X0" ]; then + # this is to prevent infinite loop in case su/sudo doesn't work: + XDUMMY_SU_EXEC=1 + export XDUMMY_SU_EXEC + + dosu=1 + nosudo="" + + for arg in $* + do + if [ "X$arg" = "X-nonroot" ]; then + dosu="" + elif [ "X$arg" = "X-nosudo" ]; then + nosudo="1" + elif [ "X$arg" = "X-help" ]; then + dosu="" + elif [ "X$arg" = "X-h" ]; then + dosu="" + elif [ "X$arg" = "X-install" ]; then + dosu="" + elif [ "X$arg" = "X-uninstall" ]; then + dosu="" + elif [ "X$arg" = "X-n" ]; then + dosu="" + elif [ "X$arg" = "X-prconf" ]; then + dosu="" + fi + done + if [ $dosu ]; then + # we need to restart it with su/sudo: + if type sudo > /dev/null 2>&1; then + : + else + nosudo=1 + fi + if [ "X$nosudo" = "X" ]; then + warn "$program: supply the sudo password to restart as root:" + if [ "X$XDUMMY_UID" != "X" ]; then + exec sudo $0 -uid $XDUMMY_UID "$@" + else + exec sudo $0 "$@" + fi + else + warn "$program: supply the root password to restart as root:" + if [ "X$XDUMMY_UID" != "X" ]; then + exec su -c "$0 -uid $XDUMMY_UID $*" + else + exec su -c "$0 $*" + fi + fi + # DONE: + exit + fi +fi + +# This will hold the X display, e.g. :20 +# +disp="" +args="" +cmdline_config="" + +# Process Xdummy args: +# +while [ "X$1" != "X" ] +do + if [ "X$1" = "X-config" -o "X$1" = "X-xf86config" ]; then + cmdline_config="$2" + fi + case $1 in + ":"*) disp=$1 + ;; + "-install") install=1; runit="" + ;; + "-uninstall") uninstall=1; runit="" + ;; + "-n") runit="" + ;; + "-no") runit="" + ;; + "-norun") runit="" + ;; + "-prconf") prconf=1; runit="" + ;; + "-notweak") notweak=1 + ;; + "-noconf") notweak=1 + ;; + "-nonroot") root="" + ;; + "-root") root=1 + ;; + "-nosudo") nosudo=1 + ;; + "-xserver") xserver="$2"; shift + ;; + "-uid") XDUMMY_UID="$2"; shift + export XDUMMY_UID + ;; + "-geom") geom="$2"; shift + ;; + "-geometry") geom="$2"; shift + ;; + "-nomodelines") nomodelines=1 + ;; + "-depth") depth="$2"; args="$args -depth $2"; + shift + ;; + "-DEPTH") depth="$2"; shift + ;; + "-tmpdir") XDUMMY_TMPDIR="$2"; shift + ;; + "-debug") debug=1 + ;; + "-nocfg") : + ;; + "-nodebug") debug="" + ;; + "-strace") strace=1 + ;; + "-ltrace") strace=2 + ;; + "-h") help; exit 0 + ;; + "-help") help; exit 0 + ;; + *) args="$args $1" + ;; + esac + shift +done + +if [ "X$XDUMMY_EXTRA_SERVER_ARGS" != "X" ]; then + args="$args $XDUMMY_EXTRA_SERVER_ARGS" +fi + +# Try to get a username for use in our tmp directory, etc. +# +user="" +if [ X`id -u` = "X0" ]; then + user=root # this will also be used below for id=0 +elif [ "X$USER" != "X" ]; then + user=$USER +elif [ "X$LOGNAME" != "X" ]; then + user=$LOGNAME +fi + +# Keep trying... +# +if [ "X$user" = "X" ]; then + user=`whoami 2>/dev/null` +fi +if [ "X$user" = "X" ]; then + user=`basename "$HOME"` +fi +if [ "X$user" = "X" -o "X$user" = "X." ]; then + user="u$$" +fi + +if [ "X$debug" = "X1" -a "X$runit" != "X" ]; then + echo "" + echo "/usr/bin/env:" + env | egrep -v '^(LS_COLORS|TERMCAP)' | sort + echo "" +fi + +# Function to compile the LD_PRELOAD shared object: +# +make_so() { + # extract code embedded in this script into a tmp C file: + n1=`grep -n '^#code_begin' $0 | head -1 | awk -F: '{print $1}'` + n2=`grep -n '^#code_end' $0 | head -1 | awk -F: '{print $1}'` + n1=`expr $n1 + 1` + dn=`expr $n2 - $n1` + + tmp=$tdir/Xdummy.$RANDOM$$.c + rm -f $tmp + if [ -e $tmp -o -h $tmp ]; then + warn "$tmp still exists." + exit 1 + fi + touch $tmp || exit 1 + tail -n +$n1 $0 | head -n $dn > $tmp + + # compile it to Xdummy.so: + if [ -f "$SO" ]; then + mv $SO $SO.$$ + rm -f $SO.$$ + fi + rm -f $SO + touch $SO + if [ ! -f "$SO" ]; then + SO=$tdir/Xdummy.$user.so + warn "warning switching LD_PRELOAD shared object to: $SO" + fi + + if [ -f "$SO" ]; then + mv $SO $SO.$$ + rm -f $SO.$$ + fi + rm -f $SO + + # we assume gcc: + if [ "X$INTERPOSE_GETUID" = "X1" ]; then + CFLAGS="$CFLAGS -DINTERPOSE_GETUID" + fi + echo "$program:" cc -shared -fPIC $CFLAGS -o $SO $tmp + cc -shared -fPIC $CFLAGS -o $SO $tmp + rc=$? + rm -f $tmp + if [ $rc != 0 ]; then + warn "$program: cannot build $SO" + exit 1 + fi + if [ "X$debug" != "X" -o "X$install" != "X" ]; then + warn "$program: created $SO" + ls -l "$SO" + fi +} + +# Set tdir to tmp dir for make_so(): +if [ "X$XDUMMY_TMPDIR" != "X" ]; then + tdir=$XDUMMY_TMPDIR + mkdir -p $tdir +else + tdir="/tmp" +fi + +# Handle -install/-uninstall case: +SO=$0.so +if [ "X$install" != "X" -o "X$uninstall" != "X" ]; then + if [ -e "$SO" -o -h "$SO" ]; then + warn "$program: removing $SO" + fi + if [ -f "$SO" ]; then + mv $SO $SO.$$ + rm -f $SO.$$ + fi + rm -f $SO + if [ -e "$SO" -o -h "$SO" ]; then + warn "warning: $SO still exists." + exit 1 + fi + if [ $install ]; then + make_so + if [ ! -f "$SO" ]; then + exit 1 + fi + fi + exit 0 +fi + +# We need a tmp directory for the .so, tweaked config file, and for +# redirecting filenames we cannot create (under -nonroot) +# +tack="" +if [ "X$XDUMMY_TMPDIR" = "X" ]; then + XDUMMY_TMPDIR="/tmp/Xdummy.$user" + + # try to tack on a unique subdir (display number or pid) + # to allow multiple instances + # + if [ "X$disp" != "X" ]; then + t0=$disp + else + t0=$1 + fi + tack=`echo "$t0" | sed -e 's/^.*://'` + if echo "$tack" | grep '^[0-9][0-9]*$' > /dev/null; then + : + else + tack=$$ + fi + if [ "X$tack" != "X" ]; then + XDUMMY_TMPDIR="$XDUMMY_TMPDIR/$tack" + fi +fi + +tmp=$XDUMMY_TMPDIR +if echo "$tmp" | grep '^/tmp' > /dev/null; then + if [ "X$tmp" != "X/tmp" -a "X$tmp" != "X/tmp/" ]; then + # clean this subdir of /tmp out, otherwise leave it... + rm -rf $XDUMMY_TMPDIR + if [ -e $XDUMMY_TMPDIR ]; then + warn "$XDUMMY_TMPDIR still exists" + exit 1 + fi + fi +fi + +mkdir -p $XDUMMY_TMPDIR +chmod 700 $XDUMMY_TMPDIR +if [ "X$tack" != "X" ]; then + chmod 700 `dirname "$XDUMMY_TMPDIR"` 2>/dev/null +fi + +# See if we can write something there: +# +tfile="$XDUMMY_TMPDIR/test.file" +touch $tfile +if [ ! -f "$tfile" ]; then + XDUMMY_TMPDIR="/tmp/Xdummy.$$.$USER" + warn "warning: setting tmpdir to $XDUMMY_TMPDIR ..." + rm -rf $XDUMMY_TMPDIR || exit 1 + mkdir -p $XDUMMY_TMPDIR || exit 1 +fi +rm -f $tfile + +export XDUMMY_TMPDIR + +# Compile the LD_PRELOAD shared object if needed (needs XDUMMY_TMPDIR) +# +if [ ! -f "$SO" ]; then + SO="$XDUMMY_TMPDIR/Xdummy.so" + make_so +fi + +# Decide which X server to use: +# +if [ "X$xserver" = "X" ]; then + if type Xorg >/dev/null 2>&1; then + xserver="Xorg" + elif type XFree86 >/dev/null 2>&1; then + xserver="XFree86" + elif -x /usr/bin/Xorg; then + xserver="/usr/bin/Xorg" + elif -x /usr/X11R6/bin/Xorg; then + xserver="/usr/X11R6/bin/Xorg" + elif -x /usr/X11R6/bin/XFree86; then + xserver="/usr/X11R6/bin/XFree86" + fi + if [ "X$xserver" = "X" ]; then + # just let it fail below. + xserver="/usr/bin/Xorg" + warn "$program: cannot locate a stock Xserver... assuming $xserver" + fi +fi + +# See if the binary is suid or not readable under -nonroot mode: +# +if [ "X$BASH_VERSION" != "X" ]; then + xserver_path=`type -p $xserver 2>/dev/null` +else + xserver_path=`type $xserver 2>/dev/null | awk '{print $NF}'` +fi +if [ -e "$xserver_path" -a "X$root" = "X" -a "X$runit" != "X" ]; then + if [ ! -r $xserver_path -o -u $xserver_path -o -g $xserver_path ]; then + # XXX not quite correct with rm -rf $XDUMMY_TMPDIR ... + # we keep on a filesystem we know root can write to. + base=`basename "$xserver_path"` + new="/tmp/$base.$user.bin" + if [ -e $new ]; then + snew=`ls -l $new | awk '{print $5}' | grep '^[0-9][0-9]*$'` + sold=`ls -l $xserver_path | awk '{print $5}' | grep '^[0-9][0-9]*$'` + if [ "X$snew" != "X" -a "X$sold" != "X" -a "X$sold" != "X$snew" ]; then + warn "removing different sized copy:" + ls -l $new $xserver_path + rm -f $new + fi + fi + if [ ! -e $new -o ! -s $new ]; then + rm -f $new + touch $new || exit 1 + chmod 700 $new || exit 1 + if [ ! -r $xserver_path ]; then + warn "" + warn "NEED TO COPY UNREADABLE $xserver_path to $new as root:" + warn "" + ls -l $xserver_path 1>&2 + warn "" + warn "This only needs to be done once:" + warn " cat $xserver_path > $new" + warn "" + nos=$nosudo + if type sudo > /dev/null 2>&1; then + : + else + nos=1 + fi + if [ "X$nos" = "X1" ]; then + warn "Please supply root passwd to 'su -c'" + su -c "cat $xserver_path > $new" + else + warn "Please supply the sudo passwd if asked:" + sudo /bin/sh -c "cat $xserver_path > $new" + fi + else + warn "" + warn "COPYING SETUID $xserver_path to $new" + warn "" + ls -l $xserver_path 1>&2 + warn "" + cat $xserver_path > $new + fi + ls -l $new + if [ -s $new ]; then + : + else + rm -f $new + ls -l $new + exit 1 + fi + warn "" + warn "Please restart Xdummy now." + exit 0 + fi + if [ ! -O $new ]; then + warn "file \"$new\" not owned by us!" + ls -l $new + exit 1 + fi + xserver=$new + fi +fi + +# Work out display: +# +if [ "X$disp" != "X" ]; then + : +elif [ "X$1" != "X" ]; then + if echo "$1" | grep '^:[0-9]' > /dev/null; then + disp=$1 + shift + elif [ "X$1" = "X:" ]; then + # ":" means for us to find one. + shift + fi +fi +if [ "X$disp" = "X" -o "X$disp" = "X:" ]; then + # try to find an open display port: + # (tcp outdated...) + ports=`netstat -ant | grep LISTEN | awk '{print $4}' | sed -e 's/^.*://'` + n=0 + while [ $n -le 20 ] + do + port=`printf "60%02d" $n` + if echo "$ports" | grep "^${port}\$" > /dev/null; then + : + else + disp=":$n" + warn "$program: auto-selected DISPLAY $disp" + break + fi + n=`expr $n + 1` + done +fi + +# Work out which vt to use, try to find/guess an open one if necessary. +# +vt="" +for arg in $* +do + if echo "$arg" | grep '^vt' > /dev/null; then + vt=$arg + break + fi +done +if [ "X$vt" = "X" ]; then + if [ "X$user" = "Xroot" ]; then + # root can user fuser(1) to see if it is in use: + if type fuser >/dev/null 2>&1; then + # try /dev/tty17 thru /dev/tty32 + n=17 + while [ $n -le 32 ] + do + dev="/dev/tty$n" + if fuser $dev >/dev/null 2>&1; then + : + else + vt="vt$n" + warn "$program: auto-selected VT $vt => $dev" + break + fi + n=`expr $n + 1` + done + fi + fi + if [ "X$vt" = "X" ]; then + # take a wild guess... + vt=vt16 + warn "$program: selected fallback VT $vt" + fi +else + vt="" +fi + +# Decide flavor of Xserver: +# +stype=`basename "$xserver"` +if echo "$stype" | grep -i xfree86 > /dev/null; then + stype=xfree86 +else + stype=xorg +fi + +tweak_config() { + in="$1" + config2="$XDUMMY_TMPDIR/xdummy_modified_xconfig.conf" + if [ "X$disp" != "X" ]; then + d=`echo "$disp" | sed -e 's,/,,g' -e 's/:/_/g'` + config2="$config2$d" + fi + + # perl script to tweak the config file... add/delete options, etc. + # + env XDUMMY_GEOM=$geom \ + XDUMMY_DEPTH=$depth \ + XDUMMY_NOMODELINES=$nomodelines \ + perl > $config2 < $in -e ' + $n = 0; + $geom = $ENV{XDUMMY_GEOM}; + $depth = $ENV{XDUMMY_DEPTH}; + $nomodelines = $ENV{XDUMMY_NOMODELINES}; + $mode_str = ""; + $videoram = "24000"; + $HorizSync = "30.0 - 130.0"; + $VertRefresh = "50.0 - 250.0"; + if ($geom ne "") { + my $tmp = ""; + foreach $g (split(/,/, $geom)) { + $tmp .= "\"$g\" "; + if (!$nomodelines && $g =~ /(\d+)x(\d+)/) { + my $w = $1; + my $h = $2; + $mode_str .= " Modeline \"$g\" "; + my $dot = sprintf("%.2f", $w * $h * 70 * 1.e-6); + $mode_str .= $dot; + $mode_str .= " " . $w; + $mode_str .= " " . int(1.02 * $w); + $mode_str .= " " . int(1.10 * $w); + $mode_str .= " " . int(1.20 * $w); + $mode_str .= " " . $h; + $mode_str .= " " . int($h + 1); + $mode_str .= " " . int($h + 3); + $mode_str .= " " . int($h + 20); + $mode_str .= "\n"; + } + } + $tmp =~ s/\s*$//; + $geom = $tmp; + } + while (<>) { + if ($ENV{XDUMMY_NOTWEAK}) { + print $_; + next; + } + $n++; + if (/^\s*#/) { + # pass comments straight thru + print; + next; + } + if (/^\s*Section\s+(\S+)/i) { + # start of Section + $sect = $1; + $sect =~ s/\W//g; + $sect =~ y/A-Z/a-z/; + $sects{$sect} = 1; + print; + next; + } + if (/^\s*EndSection/i) { + # end of Section + if ($sect eq "serverflags") { + if (!$got_DontVTSwitch) { + print " ##Xdummy:##\n"; + print " Option \"DontVTSwitch\" \"true\"\n"; + } + if (!$got_AllowMouseOpenFail) { + print " ##Xdummy:##\n"; + print " Option \"AllowMouseOpenFail\" \"true\"\n"; + } + if (!$got_PciForceNone) { + print " ##Xdummy:##\n"; + print " Option \"PciForceNone\" \"true\"\n"; + } + } elsif ($sect eq "device") { + if (!$got_Driver) { + print " ##Xdummy:##\n"; + print " Driver \"dummy\"\n"; + } + if (!$got_VideoRam) { + print " ##Xdummy:##\n"; + print " VideoRam $videoram\n"; + } + } elsif ($sect eq "screen") { + if ($depth ne "" && !got_DefaultDepth) { + print " ##Xdummy:##\n"; + print " DefaultDepth $depth\n"; + } + if ($got_Monitor eq "") { + print " ##Xdummy:##\n"; + print " Monitor \"Monitor0\"\n"; + } + } elsif ($sect eq "monitor") { + if (!got_HorizSync) { + print " ##Xdummy:##\n"; + print " HorizSync $HorizSync\n"; + } + if (!got_VertRefresh) { + print " ##Xdummy:##\n"; + print " VertRefresh $VertRefresh\n"; + } + if (!$nomodelines) { + print " ##Xdummy:##\n"; + print $mode_str; + } + } + $sect = ""; + print; + next; + } + + if (/^\s*SubSection\s+(\S+)/i) { + # start of Section + $subsect = $1; + $subsect =~ s/\W//g; + $subsect =~ y/A-Z/a-z/; + $subsects{$subsect} = 1; + if ($sect eq "screen" && $subsect eq "display") { + $got_Modes = 0; + } + print; + next; + } + if (/^\s*EndSubSection/i) { + # end of SubSection + if ($sect eq "screen") { + if ($subsect eq "display") { + if ($depth ne "" && !$set_Depth) { + print " ##Xdummy:##\n"; + print " Depth\t$depth\n"; + } + if ($geom ne "" && ! $got_Modes) { + print " ##Xdummy:##\n"; + print " Modes\t$geom\n"; + } + } + } + $subsect = ""; + print; + next; + } + + $l = $_; + $l =~ s/#.*$//; + if ($sect eq "serverflags") { + if ($l =~ /^\s*Option.*DontVTSwitch/i) { + $_ =~ s/false/true/ig; + $got_DontVTSwitch = 1; + } + if ($l =~ /^\s*Option.*AllowMouseOpenFail/i) { + $_ =~ s/false/true/ig; + $got_AllowMouseOpenFail = 1; + } + if ($l =~ /^\s*Option.*PciForceNone/i) { + $_ =~ s/false/true/ig; + $got_PciForceNone= 1; + } + } + if ($sect eq "module") { + if ($l =~ /^\s*Load.*\b(dri|fbdevhw)\b/i) { + $_ = "##Xdummy## $_"; + } + } + if ($sect eq "monitor") { + if ($l =~ /^\s*HorizSync/i) { + $got_HorizSync = 1; + } + if ($l =~ /^\s*VertRefresh/i) { + $got_VertRefresh = 1; + } + } + if ($sect eq "device") { + if ($l =~ /^(\s*Driver)\b/i) { + $_ = "$1 \"dummy\"\n"; + $got_Driver = 1; + } + if ($l =~ /^\s*VideoRam/i) { + $got_VideoRam= 1; + } + } + if ($sect eq "inputdevice") { + if ($l =~ /^\s*Option.*\bDevice\b/i) { + print " ##Xdummy:##\n"; + $_ = " Option \"Device\" \"/dev/dilbert$n\"\n"; + } + } + if ($sect eq "screen") { + if ($l =~ /^\s*DefaultDepth\s+(\d+)/i) { + if ($depth ne "") { + print " ##Xdummy:##\n"; + $_ = " DefaultDepth\t$depth\n"; + } + $got_DefaultDepth = 1; + } + if ($l =~ /^\s*Monitor\s+(\S+)/i) { + $got_Monitor = $1; + $got_Monitor =~ s/"//g; + } + if ($subsect eq "display") { + if ($geom ne "") { + if ($l =~ /^(\s*Modes)\b/i) { + print " ##Xdummy:##\n"; + $_ = "$1 $geom\n"; + $got_Modes = 1; + } + } + if ($l =~ /^\s*Depth\s+(\d+)/i) { + my $d = $1; + if (!$set_Depth && $depth ne "") { + $set_Depth = 1; + if ($depth != $d) { + print " ##Xdummy:##\n"; + $_ = " Depth\t$depth\n"; + } + } + } + } + } + print; + } + if ($ENV{XDUMMY_NOTWEAK}) { + exit; + } + # create any crucial sections that are missing: + if (! exists($sects{serverflags})) { + print "\n##Xdummy:##\n"; + print "Section \"ServerFlags\"\n"; + print " Option \"DontVTSwitch\" \"true\"\n"; + print " Option \"AllowMouseOpenFail\" \"true\"\n"; + print " Option \"PciForceNone\" \"true\"\n"; + print "EndSection\n"; + } + if (! exists($sects{device})) { + print "\n##Xdummy:##\n"; + print "Section \"Device\"\n"; + print " Identifier \"Videocard0\"\n"; + print " Driver \"dummy\"\n"; + print " VideoRam $videoram\n"; + print "EndSection\n"; + } + if (! exists($sects{monitor})) { + print "\n##Xdummy:##\n"; + print "Section \"Monitor\"\n"; + print " Identifier \"Monitor0\"\n"; + print " HorizSync $HorizSync\n"; + print " VertRefresh $VertRefresh\n"; + print "EndSection\n"; + } + if (! exists($sects{screen})) { + print "\n##Xdummy:##\n"; + print "Section \"Screen\"\n"; + print " Identifier \"Screen0\"\n"; + print " Device \"Videocard0\"\n"; + if ($got_Monitor ne "") { + print " Monitor \"$got_Monitor\"\n"; + } else { + print " Monitor \"Monitor0\"\n"; + } + if ($depth ne "") { + print " DefaultDepth $depth\n"; + } else { + print " DefaultDepth 24\n"; + } + print " SubSection \"Display\"\n"; + print " Viewport 0 0\n"; + print " Depth 24\n"; + if ($got_Modes) { + ; + } elsif ($geom ne "") { + print " Modes $geom\n"; + } else { + print " Modes \"1280x1024\" \"1024x768\" \"800x600\"\n"; + } + print " EndSubSection\n"; + print "EndSection\n"; + } +'; +} + +# Work out config file and tweak it. +# +if [ "X$cmdline_config" = "X" ]; then + : +elif [ "X$cmdline_config" = "Xxdummy-builtin" ]; then + : +elif echo "$cmdline_config" | grep '/' > /dev/null; then + : +else + # ignore basename only case (let server handle it) + cmdline_config="" + notweak=1 +fi + +config=$cmdline_config + +if [ "X$notweak" = "X1" -a "X$root" = "X" -a -f "$cmdline_config" ]; then + # if not root we need to copy (but not tweak) the specified config. + XDUMMY_NOTWEAK=1 + export XDUMMY_NOTWEAK + notweak="" +fi + +if [ ! $notweak ]; then + # tweaked config will be put in $config2: + config2="" + if [ "X$config" = "X" ]; then + # use the default one: + if [ "X$stype" = "Xxorg" ]; then + config=/etc/X11/xorg.conf + else + if [ -f "/etc/X11/XF86Config-4" ]; then + config="/etc/X11/XF86Config-4" + else + config="/etc/X11/XF86Config" + fi + fi + if [ ! -f "$config" ]; then + for c in /etc/X11/xorg.conf /etc/X11/XF86Config-4 /etc/X11/XF86Config + do + if [ -f $c ]; then + config=$c + break + fi + done + fi + fi + + if [ "X$config" = "Xxdummy-builtin" ]; then + config="" + fi + + if [ ! -f "$config" ]; then + config="$XDUMMY_TMPDIR/xorg.conf" + warn "$program: using minimal built-in xorg.conf settings." + cat > $config < /dev/null; then + so=`echo "$so" | sed -e "s,^\.,$pwd,"` + fi + if echo "$so" | grep '/' > /dev/null; then + : + else + so="$pwd/$so" + fi + warn "env LD_PRELOAD=$so $xserver $disp $args $vt" + warn "" + if [ ! $runit ]; then + exit 0 + fi +fi + +if [ $strace ]; then + if [ "X$strace" = "X2" ]; then + ltrace -f env LD_PRELOAD=$SO $xserver $disp $args $vt + else + strace -f env LD_PRELOAD=$SO $xserver $disp $args $vt + fi +else + exec env LD_PRELOAD=$SO $xserver $disp $args $vt +fi + +exit $? + +######################################################################### + +code() { +#code_begin +#include +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 +#define O_FSYNC O_SYNC +#define O_ASYNC 020000 + +#include +#include +#include + +#include +#include + +#define __USE_GNU +#include + +static char tmpdir[4096]; +static char str1[4096]; +static char str2[4096]; + +static char devs[256][1024]; +static int debug = -1; +static int root = -1; +static int changed_uid = 0; +static int saw_fonts = 0; +static int saw_lib_modules = 0; + +static time_t start = 0; + +void check_debug(void) { + if (debug < 0) { + if (getenv("XDUMMY_DEBUG") != NULL) { + debug = 1; + } else { + debug = 0; + } + /* prevent other processes using the preload: */ + putenv("LD_PRELOAD="); + } +} +void check_root(void) { + if (root < 0) { + /* script tells us if we are root */ + if (getenv("XDUMMY_ROOT") != NULL) { + root = 1; + } else { + root = 0; + } + } +} + +void check_uid(void) { + if (start == 0) { + start = time(NULL); + if (debug) fprintf(stderr, "START: %u\n", (unsigned int) start); + return; + } else if (changed_uid == 0) { + if (saw_fonts || time(NULL) > start + 20) { + if (getenv("XDUMMY_UID")) { + int uid = atoi(getenv("XDUMMY_UID")); + if (debug) fprintf(stderr, "SETREUID: %d saw_fonts=%d\n", uid, saw_fonts); + if (uid >= 0) { + /* this will simply fail in -nonroot mode: */ + setreuid(uid, -1); + } + } + changed_uid = 1; + } + } +} + +#define CHECKIT if (debug < 0) check_debug(); \ + if (root < 0) check_root(); \ + check_uid(); + +static void set_tmpdir(void) { + char *s; + static int didset = 0; + if (didset) { + return; + } + s = getenv("XDUMMY_TMPDIR"); + if (! s) { + s = "/tmp"; + } + tmpdir[0] = '\0'; + strcat(tmpdir, s); + strcat(tmpdir, "/"); + didset = 1; +} + +static char *tmpdir_path(const char *path) { + char *str; + set_tmpdir(); + strcpy(str2, path); + str = str2; + while (*str) { + if (*str == '/') { + *str = '_'; + } + str++; + } + strcpy(str1, tmpdir); + strcat(str1, str2); + return str1; +} + +int open(const char *pathname, int flags, unsigned short mode) { + int fd; + char *store_dev = NULL; + static int (*real_open)(const char *, int , unsigned short) = NULL; + + CHECKIT + if (! real_open) { + real_open = (int (*)(const char *, int , unsigned short)) + dlsym(RTLD_NEXT, "open"); + } + + if (strstr(pathname, "lib/modules/")) { + /* not currently used. */ + saw_lib_modules = 1; + } + + if (!root) { + if (strstr(pathname, "/dev/") == pathname) { + store_dev = strdup(pathname); + } + if (strstr(pathname, "/dev/tty") == pathname && strcmp(pathname, "/dev/tty")) { + pathname = tmpdir_path(pathname); + if (debug) fprintf(stderr, "OPEN: %s -> %s (as FIFO)\n", store_dev, pathname); + /* we make it a FIFO so ioctl on it does not fail */ + unlink(pathname); + mkfifo(pathname, 0666); + } else if (0) { + /* we used to handle more /dev files ... */ + fd = real_open(pathname, O_WRONLY|O_CREAT, 0777); + close(fd); + } + } + + fd = real_open(pathname, flags, mode); + + if (debug) fprintf(stderr, "OPEN: %s %d %d fd=%d\n", pathname, flags, mode, fd); + + if (! root) { + if (store_dev) { + if (fd < 256) { + strcpy(devs[fd], store_dev); + } + free(store_dev); + } + } + + return(fd); +} + +int open64(const char *pathname, int flags, unsigned short mode) { + int fd; + + CHECKIT + if (debug) fprintf(stderr, "OPEN64: %s %d %d\n", pathname, flags, mode); + + fd = open(pathname, flags, mode); + return(fd); +} + +int rename(const char *oldpath, const char *newpath) { + static int (*real_rename)(const char *, const char *) = NULL; + + CHECKIT + if (! real_rename) { + real_rename = (int (*)(const char *, const char *)) + dlsym(RTLD_NEXT, "rename"); + } + + if (debug) fprintf(stderr, "RENAME: %s %s\n", oldpath, newpath); + + if (root) { + return(real_rename(oldpath, newpath)); + } + + if (strstr(oldpath, "/var/log") == oldpath) { + if (debug) fprintf(stderr, "RENAME: returning 0\n"); + return 0; + } + return(real_rename(oldpath, newpath)); +} + +FILE *fopen(const char *pathname, const char *mode) { + static FILE* (*real_fopen)(const char *, const char *) = NULL; + char *str; + + if (! saw_fonts) { + if (strstr(pathname, "/fonts/")) { + if (strstr(pathname, "fonts.dir")) { + saw_fonts = 1; + } else if (strstr(pathname, "fonts.alias")) { + saw_fonts = 1; + } + } + } + + CHECKIT + if (! real_fopen) { + real_fopen = (FILE* (*)(const char *, const char *)) + dlsym(RTLD_NEXT, "fopen"); + } + + if (debug) fprintf(stderr, "FOPEN: %s %s\n", pathname, mode); + + if (strstr(pathname, "xdummy_modified_xconfig.conf")) { + /* make our config appear to be in /etc/X11, etc. */ + char *q = strrchr(pathname, '/'); + if (q != NULL && getenv("XDUMMY_TMPDIR") != NULL) { + strcpy(str1, getenv("XDUMMY_TMPDIR")); + strcat(str1, q); + if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str1); + pathname = str1; + } + } + + if (root) { + return(real_fopen(pathname, mode)); + } + + str = (char *) pathname; + if (strstr(pathname, "/var/log") == pathname) { + str = tmpdir_path(pathname); + if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str); + } + return(real_fopen(str, mode)); +} + + +#define RETURN0 if (debug) \ + {fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return 0; +#define RETURN1 if (debug) \ + {fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return -1; + +int ioctl(int fd, int req, void *ptr) { + static int closed_xf86Info_consoleFd = 0; + static int (*real_ioctl)(int, int , void *) = NULL; + + CHECKIT + if (! real_ioctl) { + real_ioctl = (int (*)(int, int , void *)) + dlsym(RTLD_NEXT, "open"); + } + if (debug) fprintf(stderr, "IOCTL: %d 0x%x %p\n", fd, req, ptr); + + /* based on xorg-x11-6.8.1-dualhead.patch */ + if (req == VT_GETMODE) { + /* close(xf86Info.consoleFd) */ + if (0 && ! closed_xf86Info_consoleFd) { + /* I think better not to close it... */ + close(fd); + closed_xf86Info_consoleFd = 1; + } + RETURN0 + } else if (req == VT_SETMODE) { + RETURN0 + } else if (req == VT_GETSTATE) { + RETURN0 + } else if (req == KDSETMODE) { + RETURN0 + } else if (req == KDSETLED) { + RETURN0 + } else if (req == KDGKBMODE) { + RETURN0 + } else if (req == KDSKBMODE) { + RETURN0 + } else if (req == VT_ACTIVATE) { + RETURN0 + } else if (req == VT_WAITACTIVE) { + RETURN0 + } else if (req == VT_RELDISP) { + if (ptr == (void *) 1) { + RETURN1 + } else if (ptr == (void *) VT_ACKACQ) { + RETURN0 + } + } + + return(real_ioctl(fd, req, ptr)); +} + +typedef void (*sighandler_t)(int); +#define SIGUSR1 10 +#define SIG_DFL ((sighandler_t)0) + +sighandler_t signal(int signum, sighandler_t handler) { + static sighandler_t (*real_signal)(int, sighandler_t) = NULL; + + CHECKIT + if (! real_signal) { + real_signal = (sighandler_t (*)(int, sighandler_t)) + dlsym(RTLD_NEXT, "signal"); + } + + if (debug) fprintf(stderr, "SIGNAL: %d %p\n", signum, handler); + + if (signum == SIGUSR1) { + if (debug) fprintf(stderr, "SIGNAL: skip SIGUSR1\n"); + return SIG_DFL; + } + + return(real_signal(signum, handler)); +} + +int close(int fd) { + static int (*real_close)(int) = NULL; + + CHECKIT + if (! real_close) { + real_close = (int (*)(int)) dlsym(RTLD_NEXT, "close"); + } + + if (debug) fprintf(stderr, "CLOSE: %d\n", fd); + if (!root) { + if (fd < 256) { + devs[fd][0] = '\0'; + } + } + return(real_close(fd)); +} + +struct stat { + int foo; +}; + +int stat(const char *path, struct stat *buf) { + static int (*real_stat)(const char *, struct stat *) = NULL; + + CHECKIT + if (! real_stat) { + real_stat = (int (*)(const char *, struct stat *)) + dlsym(RTLD_NEXT, "stat"); + } + + if (debug) fprintf(stderr, "STAT: %s\n", path); + + return(real_stat(path, buf)); +} + +int stat64(const char *path, struct stat *buf) { + static int (*real_stat64)(const char *, struct stat *) = NULL; + + CHECKIT + if (! real_stat64) { + real_stat64 = (int (*)(const char *, struct stat *)) + dlsym(RTLD_NEXT, "stat64"); + } + + if (debug) fprintf(stderr, "STAT64: %s\n", path); + + return(real_stat64(path, buf)); +} + +int chown(const char *path, uid_t owner, gid_t group) { + static int (*real_chown)(const char *, uid_t, gid_t) = NULL; + + CHECKIT + if (! real_chown) { + real_chown = (int (*)(const char *, uid_t, gid_t)) + dlsym(RTLD_NEXT, "chown"); + } + + if (root) { + return(real_chown(path, owner, group)); + } + + if (debug) fprintf(stderr, "CHOWN: %s %d %d\n", path, owner, group); + + if (strstr(path, "/dev") == path) { + if (debug) fprintf(stderr, "CHOWN: return 0\n"); + return 0; + } + + return(real_chown(path, owner, group)); +} + +extern int *__errno_location (void); +#ifndef ENODEV +#define ENODEV 19 +#endif + +int ioperm(unsigned long from, unsigned long num, int turn_on) { + static int (*real_ioperm)(unsigned long, unsigned long, int) = NULL; + + CHECKIT + if (! real_ioperm) { + real_ioperm = (int (*)(unsigned long, unsigned long, int)) + dlsym(RTLD_NEXT, "ioperm"); + } + if (debug) fprintf(stderr, "IOPERM: %d %d %d\n", (int) from, (int) num, turn_on); + if (root) { + return(real_ioperm(from, num, turn_on)); + } + if (from == 0 && num == 1024 && turn_on == 1) { + /* we want xf86EnableIO to fail */ + if (debug) fprintf(stderr, "IOPERM: setting ENODEV.\n"); + *__errno_location() = ENODEV; + return -1; + } + return 0; +} + +int iopl(int level) { + static int (*real_iopl)(int) = NULL; + + CHECKIT + if (! real_iopl) { + real_iopl = (int (*)(int)) dlsym(RTLD_NEXT, "iopl"); + } + if (debug) fprintf(stderr, "IOPL: %d\n", level); + if (root) { + return(real_iopl(level)); + } + return 0; +} + +#ifdef INTERPOSE_GETUID + +/* + * we got things to work w/o pretending to be root. + * so we no longer interpose getuid(), etc. + */ + +uid_t getuid(void) { + static uid_t (*real_getuid)(void) = NULL; + CHECKIT + if (! real_getuid) { + real_getuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid"); + } + if (root) { + return(real_getuid()); + } + if (debug) fprintf(stderr, "GETUID: 0\n"); + return 0; +} +uid_t geteuid(void) { + static uid_t (*real_geteuid)(void) = NULL; + CHECKIT + if (! real_geteuid) { + real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid"); + } + if (root) { + return(real_geteuid()); + } + if (debug) fprintf(stderr, "GETEUID: 0\n"); + return 0; +} +uid_t geteuid_kludge1(void) { + static uid_t (*real_geteuid)(void) = NULL; + CHECKIT + if (! real_geteuid) { + real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid"); + } + if (debug) fprintf(stderr, "GETEUID: 0 saw_libmodules=%d\n", saw_lib_modules); + if (root && !saw_lib_modules) { + return(real_geteuid()); + } else { + saw_lib_modules = 0; + return 0; + } +} + +uid_t getuid32(void) { + static uid_t (*real_getuid32)(void) = NULL; + CHECKIT + if (! real_getuid32) { + real_getuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid32"); + } + if (root) { + return(real_getuid32()); + } + if (debug) fprintf(stderr, "GETUID32: 0\n"); + return 0; +} +uid_t geteuid32(void) { + static uid_t (*real_geteuid32)(void) = NULL; + CHECKIT + if (! real_geteuid32) { + real_geteuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid32"); + } + if (root) { + return(real_geteuid32()); + } + if (debug) fprintf(stderr, "GETEUID32: 0\n"); + return 0; +} + +gid_t getgid(void) { + static gid_t (*real_getgid)(void) = NULL; + CHECKIT + if (! real_getgid) { + real_getgid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid"); + } + if (root) { + return(real_getgid()); + } + if (debug) fprintf(stderr, "GETGID: 0\n"); + return 0; +} +gid_t getegid(void) { + static gid_t (*real_getegid)(void) = NULL; + CHECKIT + if (! real_getegid) { + real_getegid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid"); + } + if (root) { + return(real_getegid()); + } + if (debug) fprintf(stderr, "GETEGID: 0\n"); + return 0; +} +gid_t getgid32(void) { + static gid_t (*real_getgid32)(void) = NULL; + CHECKIT + if (! real_getgid32) { + real_getgid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid32"); + } + if (root) { + return(real_getgid32()); + } + if (debug) fprintf(stderr, "GETGID32: 0\n"); + return 0; +} +gid_t getegid32(void) { + static gid_t (*real_getegid32)(void) = NULL; + CHECKIT + if (! real_getegid32) { + real_getegid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid32"); + } + if (root) { + return(real_getegid32()); + } + if (debug) fprintf(stderr, "GETEGID32: 0\n"); + return 0; +} +#endif + +#if 0 +/* maybe we need to interpose on strcmp someday... here is the template */ +int strcmp(const char *s1, const char *s2) { + static int (*real_strcmp)(const char *, const char *) = NULL; + CHECKIT + if (! real_strcmp) { + real_strcmp = (int (*)(const char *, const char *)) dlsym(RTLD_NEXT, "strcmp"); + } + if (debug) fprintf(stderr, "STRCMP: '%s' '%s'\n", s1, s2); + return(real_strcmp(s1, s2)); +} +#endif + +#code_end +}