#!/bin/bash # $Id:$ # # This provides a stripped down 'failsafe' mode for situations # where X is failing to start up. # Author: Bryce W. Harrington # Copyright 2007 Canonical, Ltd # # This is free software; you may 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, # or (at your option) any later version. # # This 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 with # the Debian operating system, in /usr/share/common-licenses/GPL; if # not, write to the Free Software Foundation, Inc., 59 Temple Place, # Suite 330, Boston, MA 02111-1307 USA xorg_conf_failsafe=${BPX_XORG_CONF_FAILSAFE:-"/etc/X11/xorg.conf.failsafe"} xorg_conf=${BPX_XORG_CONF:-"/etc/X11/xorg.conf"} run_dexconf=${BPX_RUN_DEXCONF:-"yes"} # TODO: This should be set to "vga", however I have been unable to # succeed in getting the vga driver running in anything over # 320x200, which is unusable. So fallback is disabled for now. #fallback_driver="vga" fallback_driver=${BPX_FALLBACK_DRIVER:-"vesa"} client=${BPX_CLIENT:-"/etc/gdm/failsafeXinit"} clientargs=${BPX_CLIENTARGS:-$xorg_conf_failsafe} blacklist=${BPX_BLACKLIST:-"/etc/gdm/failsafeBlacklist"} main_driver=${BPX_DRIVER:-"vesa"} checkduration=${BPX_CHECK_DURATION:-30} failsafe_log=${BPX_LOG:-"/var/log/gdm/failsafe.log"} server=${BPX_SERVER:-/usr/bin/X} serverargs=${BPX_SERVERARGS:-"$*"} if [ -z $serverargs ]; then # Use :10 to avoid overwriting the (failed) Xorg.0.log serverargs=":10" fi serverargs="${serverargs} -br -once -config $xorg_conf_failsafe" # -br: Black background # -once: Terminate server after one session # -config: Specify location of xorg.conf file to use # Note: Only root can specify absolute paths warn() { echo "Warning: $1" 1>&2 } is_installed() { prog=$1 need=$2 /usr/bin/which $prog > /dev/null 2>&1 err=$? if [ ! $err = 0 ]; then warn "Could not $need because $prog is not installed ($err)" return $err fi return 0 } # Tests if the given pciids are in numerical order from least to greatest # (e.g., $a <= $b <= $c <= ...) pciids_in_order() { lastid=0 for pciid in $* ; do # Strip embedded : and convert hex to dec id=$((0x${pciid/:/})) if [ $id -lt $lastid ]; then return 1 fi lastid=$id done return 0 } get_edid() { # Retrieve EDID (if get-edid is installed) is_installed get-edid "retrieve EDID" || return 1 # Discard stderr, which is text data about the card get-edid 2>/dev/null } get_pciids() { # Retrieve PCI IDs from discover is_installed discover "retreive PCI IDs" || return 1 discover --enable-all video --format="%i\n" } get_driver() { EDID=$(get_edid) PCIIDS=$(get_pciids) if [ "x$EDID" = "x" ]; then echo $fallback_driver return 1 elif [ "x$PCIIDS" = "x" ]; then echo $fallback_driver return 2 fi # TODO: What if we have multiple pciids? Assume first for now. pciid=$(echo $PCIIDS | head -n 1) EDID_MD5=$(echo $EDID | md5sum | head -n1 | cut -d" " -f1) matches=$(egrep "^$EDID_MD5|^ANY" $blacklist) found="no" for line in "$matches"; do line=$(echo $line | sed -e "s/ \+/ /") range=$(echo $line | cut -d' ' -f 2) driver=$(echo $line | cut -d' ' -f 3) pciid1=$(echo $range | cut -d- -f 1) pciid2=$(echo $range | cut -d- -f 2) if [ "x$pciid1" = "x" ]; then continue elif [ "x$pciid1" = "xANY" ]; then found="yes" break elif [ "$pciid1" = "$pciid" ]; then found="yes" break elif [ "x$pciid2" = "x" ]; then continue elif pciids_in_order $pciid1 $pciid $pciid2 ; then found="yes" break fi done if [ $found = "no" ]; then echo $main_driver else # No driver was specified - assume vga if [ "x$driver" = "x" ]; then warn "System is blacklisted, but no driver specified; assuming fallback" driver=$fallback_driver fi echo $driver fi return 0 } # Check if we've already attempted a failsafe session without success if [ -e "$failsafe_log" ]; then cur_time=$(date +"%s") last_run=$(tail -n 1 $failsafe_log | cut -d' ' -f1) time_diff=$(expr $cur_time - $last_run) if [ $time_diff -lt $checkduration ]; then warn "Failsafe mode was already attempted within $checkduration seconds." warn "Falling back to gdm to report the issue." exit 1 fi fi # When failsafe mode is activated, check the blacklist for systems we # know do not support VESA 800x600/256 # Use EDID + PCI IDs as key to lookup (Can get PCI IDs from discover) # If the display does not give EDID info, then use VGA 640x480/16 mode # If a matching entry is found, then use VGA 640x480/16 mode driver=$(get_driver) if [ "x${run_dexconf}" = "xyes" ]; then # Generate an appropriate xorg.conf /etc/gdm/failsafeDexconf $driver $xorg_conf_failsafe if [ ! -s $xorg_conf_failsafe ]; then warn "Could not generate $xorg_conf_failsafe for $driver driver" exit 1 fi elif [ ! -s $xorg_conf_failsafe ]; then warn "Requested to use $xorg_conf_failsafe for $driver driver, but it does not exist" exit 1 fi md5xorg=$(md5sum $xorg_conf) date +"%s $md5xorg" >> $failsafe_log if [ $? -ne 0 ]; then warn "Cannot write to $failsafe_log" fi # TODO: Start up the failsafe X session using their regular user account if pidof /usr/sbin/gdm ; then clientargs="${clientargs} with-gdm" fi echo "xinit $client $clientargs -- $server $serverargs" xinit $client $clientargs -- $server $serverargs & sleep 3 # Stop gdm if it's running, otherwise it will attempt to manage the display # out from under us if pidof /usr/sbin/gdm ; then exec kill -STOP $PPID fi # This seems to cause gdm to attempt to start a new x session #exec kill -USR1 `cat /var/run/gdm.pid`