#!/bin/sh
set -e
export LC_ALL=C

type=$1
preversion=$2

rm_conffile() {
    local PKGNAME="$1"
    local CONFFILE="$2"

    [ -e "$CONFFILE" ] || return 0

    local md5sum="$(md5sum $CONFFILE | sed -e 's/ .*//')"
    local old_md5sum="$(dpkg-query -W -f='${Conffiles}' $PKGNAME | \
            sed -n -e "\' $CONFFILE ' { s/ obsolete$//; s/.* //; p }")"
    if [ "$md5sum" != "$old_md5sum" ]; then
        echo "Obsolete conffile $CONFFILE has been modified by you."
        echo "Saving as $CONFFILE.dpkg-bak ..."
        mv -f "$CONFFILE" "$CONFFILE".dpkg-bak
    else
        echo "Removing obsolete conffile $CONFFILE ..."
        rm -f "$CONFFILE"
    fi
}

linux_compare_versions () {
    verA=$(($(echo "$1" | sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1 \* 10000 + \2 \* 100 + \3/')))
    verB=$(($(echo "$3" | sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1 \* 10000 + \2 \* 100 + \3/')))

    test $verA -$2 $verB
}

kfreebsd_compare_versions () {
    verA=$(($(echo "$1" | sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \* 100 + \2/')))
    verB=$(($(echo "$3" | sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \* 100 + \2/')))

    test $verA -$2 $verB
}

kernel26_help() {
    echo ""
    echo "The installation of a 2.6 kernel _could_ ask you to install a new libc"
    echo "first, this is NOT a bug, and should *NOT* be reported. In that case,"
    echo "please add lenny sources to your /etc/apt/sources.list and run:"
    echo "  apt-get install -t lenny linux-image-2.6"
    echo "Then reboot into this new kernel, and proceed with your upgrade"
}

if [ "$type" = upgrade ]
then
    # Remove old /etc/init.d/glibc.sh init script
    if dpkg --compare-versions "$preversion" le "2.9-22"; then
        rm_conffile libc6 "/etc/init.d/glibc.sh"
	update-rc.d glibc.sh remove >/dev/null
    fi

    # Load debconf module if available
    if [ -f /usr/share/debconf/confmodule ] ; then
        . /usr/share/debconf/confmodule
    fi

    if [ -n "$preversion" ]; then
	# NSS authentication trouble guard
	if dpkg --compare-versions "$preversion" lt 2.11-1; then
	    if pidof xscreensaver xlockmore >/dev/null; then
		if [ -f /usr/share/debconf/confmodule ] ; then
		    db_version 2.0
		    db_reset glibc/disable-screensaver
		    db_input critical glibc/disable-screensaver || true
		    db_go || true
		else
		    echo "xscreensaver and xlockmore must be restarted before upgrading"
		    echo
		    echo "One or more running instances of xscreensaver or xlockmore have been"
		    echo "detected on this system. Because of incompatible library changes, the"
		    echo "upgrade of the GNU C library will leave you unable to authenticate to"
		    echo "these programs. You should arrange for these programs to be restarted"
		    echo "or stopped before continuing this upgrade, to avoid locking your users"
		    echo "out of their current sessions."
		    echo
		    frontend=`echo "$DEBIAN_FRONTEND" | tr '[:upper:]' '[:lower:]'`
		    if [ "$frontend" = noninteractive ]; then
			echo "Non-interactive mode, upgrade glibc forcibly"
		    else
		        echo -n "Press a key to continue"
		        read answer
		    fi
		    echo
		fi
	    fi

	    check="kdm postgresql xdm"
	    # NSS services check: NSS_CHECK
	    echo -n "Checking for services that may need to be restarted..."
	    # Only get the ones that are installed, and configured
	    check=$(dpkg -s $check 2> /dev/null | egrep '^Package:|^Status:' | awk '{if ($1 ~ /^Package:/) { package=$2 } else if ($0 ~ /^Status: .* installed$/) { print package }}')
	    # some init scripts don't match the package names
	    check=$(echo $check | \
	    	    sed -e's/\bapache2.2-common\b/apache2/g' \
	    	    	-e's/\bat\b/atd/g' \
	    	    	-e's/\bdovecot-common\b/dovecot/g' \
	    	    	-e's/\bexim4-base\b/exim4/g' \
	    	    	-e's/\blpr\b/lpd/g' \
	    	    	-e's/\blpr-ppd\b/lpd-ppd/g' \
	    	    	-e's/\bmysql-server\b/mysql/g' \
	    	    	-e's/\bsasl2-bin\b/saslauthd/g' \
	    )
	    echo
	    echo "Checking init scripts..."
	    rl=$(runlevel | sed 's/.*\ //')
	    for service in $check; do
	    	if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
	    	    invoke-rc.d ${service} status >/dev/null 2>/dev/null && status=0 || status=$?
	    	    if [ "$status" = "0" ] || [ "$status" = "2" ] ; then
	    	    	services="$service $services"
	    	    elif [ "$status" = "100" ] ; then
	    	    	echo "WARNING: init script for $service not found."
	    	    fi
	    	else
	    	    if [ -f /usr/share/file-rc/rc ] || [ -f /usr/lib/file-rc/rc ] && [ -f /etc/runlevel.conf ]; then
	    	    	idl=$(filerc $rl $service)
	    	    else
	    	    	idl=$(ls /etc/rc${rl}.d/S??${service} 2> /dev/null | head -1)
	    	    fi
	    	    if [ -n "$idl" ] && [ -x $idl ]; then
	    	    	services="$service $services"
	    	    fi
	    	fi
	    done
	    if [ -n "$services" ]; then 
		if [ -f /usr/share/debconf/confmodule ] ; then
	            db_version 2.0
		    db_reset glibc/upgrade
		    db_subst glibc/upgrade services $services
		    db_input critical glibc/upgrade || true
		    db_go || true
		    db_get glibc/upgrade
		    answer=$RET
		else
		    echo "Do you want to upgrade glibc now?"
		    echo
		    echo "Running services and programs that are using NSS need to be restarted,"
		    echo "otherwise they might not be able to do lookup or authentication any more."
		    echo "The installation process is able to restart some services (such as ssh or"
		    echo "telnetd), but other programs cannot be restarted automatically.  One such"
		    echo "program that needs manual stopping and restart after the glibc upgrade by"
		    echo "yourself is xdm - because automatic restart might disconnect your active"
		    echo "X11 sessions."
		    echo
		    echo "This script detected the following installed services which must be"
		    echo "stopped before the upgrade: $services"
		    echo
		    echo "If you want to interrupt the upgrade now and continue later, please"
		    echo "answer No to the question below."
		    echo 
		    frontend=`echo "$DEBIAN_FRONTEND" | tr '[:upper:]' '[:lower:]'`
		    if [ "$frontend" = noninteractive ]; then
			echo "Non-interactive mode, upgrade glibc forcibly"
			answer=true
		    else
		        echo -n "Do you want to upgrade glibc now? [Y/n] "
		        read answer
		        case $answer in
			    Y*|y*) answer=true ;;
			    N*|n*) answer=false ;;
			    *) answer=true ;;
			esac
		    fi
		    echo
		fi

		if [ "x$answer" != "xtrue" ]; then
	            echo "Stopped glibc upgrade.  Please retry the upgrade after you have"
	            echo "checked or stopped services by hand."
	            exit 1
		fi
	    fi
	fi # end upgrading and $preversion lt 2.11-1
    fi # Upgrading

    # This will keep us from using hwcap libs (optimized) during an
    # upgrade.
    touch /etc/ld.so.nohwcap
fi

# Sanity check.
# If there are versions of glibc outside of the normal installation
# location (/lib, /lib64, etc.) then things may break very badly
# as soon as ld.so is replaced by a new version.  This check is not
# foolproof, but it's pretty accurate.  This script ignores libraries
# with different sonames, and libraries incompatible with the 
# to-be-installed ld.so.

check_dirs () {
  for dir in $*; do
    # Follow symlinks
    dirlink=$(readlink -e $dir)
    [ -n "$dirlink" ] && dir=$dirlink 
    
    # Handle /lib in LD_LIBRARY_PATH.
    if expr $dir : "/lib.*" > /dev/null; then
      continue
    fi
    # Skip ia32-libs package on ia64, and similar libraries
    # (not sure why these get added to /etc/ld.so.conf)
    if expr $dir : "/emul/.*" > /dev/null; then
      continue
    fi
    if test -d $dir; then
      output=$(ls $dir | egrep '^(libc.so.6|libm.so.6|libpthread.so.0|librt.so.1|libdl.so.2)$' 2>/dev/null)

      if test -n "$output"; then
	# See if the found libraries are compatible with the system ld.so;
	# if they aren't, they'll be ignored.  Check e_ident, e_type (which
	# will just be ET_DYN), and e_machine.  If a match is found, there
	# is a risk of breakage.
	for lib in $output
	do
	  if test -f "$dir/$lib"; then
	    libbytes=`head -c 20 $dir/$lib | od -c`
	    if test "$ldbytes" = "$libbytes"; then
	      echo "Matching libraries: $dir/$lib"
	      return 0
	    fi
	  fi
	done
      fi
    fi
  done
  return 1
}

if [ "$type" != abort-upgrade ]
then
  ldbytes=`head -c 20 /lib64/ld-linux-x86-64.so.2 | od -c`
  dirs="/lib32 /lib64 /usr/local/lib /usr/local/lib32 /usr/local/lib64"
  if ! test -L /usr; then
    dirs="$dirs /usr/lib /usr/lib32 /usr/lib64"
  fi
  if check_dirs $dirs; then
    echo
    echo "A copy of glibc was found in an unexpected directory."
    echo "It is not safe to upgrade the C library in this situation;"
    echo "please remove that copy of the C library and try again."
    exit 1
  fi

  if test -n "$LD_LIBRARY_PATH"; then
    dirs=$(echo $LD_LIBRARY_PATH | sed 's/:/ /')
    if check_dirs $dirs; then
      echo
      echo "Another copy of the C library was found via LD_LIBRARY_PATH."
      echo "It is not safe to upgrade the C library in this situation;"
      echo "please remove the directory from LD_LIBRARY_PATH and try again."
      exit 1
    fi
  fi
  if test -e /etc/ld.so.conf; then
    dirs=$(echo $(cat /etc/ld.so.conf))
    if check_dirs $dirs; then
      echo
      echo "Another copy of the C library was found via /etc/ld.so.conf."
      echo "It is not safe to upgrade the C library in this situation;"
      echo "please remove the directory from /etc/ld.so.conf and try again."
      exit 1
    fi
  fi
  for i in ld-2.3.2.so libc-2.3.2.so ld-2.3.6.so libc-2.3.6.so ; do
    if [ -e /lib/tls/$i ] && ! dpkg-query -L libc6 2>/dev/null | grep -q /lib/tls/$i ; then
      echo
      echo "A non-dpkg owned copy of the C library was found in /lib/tls."
      echo "It is not safe to upgrade the C library in this situation;"
      echo "please remove that copy of the C library and try again."
      exit 1
    fi
  done
  if [ -e /lib/tls/i686/cmov/libc.so.6 ] || [ -e /lib/i686/cmov/libc.so.6 ] ; then
    status_i686=$(dpkg -s libc6-i686 2>/dev/null | grep ^Status: | sed -e 's/^Status: \(.*\) \(.*\) \(.*\)/\3/g')
    status_xen=$(dpkg -s libc6-xen 2>/dev/null | grep ^Status: | sed -e 's/^Status: \(.*\) \(.*\) \(.*\)/\3/g')
    if ([ -z "$status_i686" ] || [ "$status_i686" = "not-installed" ] || [ "$status_i686" = "config-files" ]) && \
       ([ -z "$status_xen" ] || [ "$status_xen" = "not-installed" ] || [ "$status_xen" = "config-files" ]); then
      echo
      echo "A non-dpkg owned copy of the libc6-i686 package was found."
      echo "It is not safe to upgrade the C library in this situation;"
      echo "please remove that copy of the C library and try again."
      exit 1
    fi
  fi
  if [ -n "$LD_ASSUME_KERNEL" ] ; then
    if dpkg --compare-versions "$LD_ASSUME_KERNEL" le "2.6.1"; then
      echo
      echo "POSIX threads library NPTL requires kernel version 2.6.1"
      echo "or later.  It appears that LD_ASSUME_KERNEL is set to $LD_ASSUME_KERNEL."
      echo "It is not safe to upgrade the C library in this situation;"
      echo "Please unset this environment variable and try again."
      exit 1
    fi
  fi

  # glibc kernel version check
  system=`uname -s`
  if [ "$system" = "Linux" ]
  then
      # Test to make sure z < 255, in x.y.z-n form of kernel version
      # Also make sure we don't trip on x.y.zFOO-n form
      kernel_rev=$(uname -r | sed 's/\([0-9]*\.[0-9]*\.\)\([0-9]*\)\(.*\)/\2/')
      if [ "$kernel_rev" -ge 255 ]
      then
          echo "WARNING: Your kernel version indicates a revision number"
          echo "of 255 or greater.  Glibc has a number of built in"
          echo "assumptions that this revision number is less than 255."
          echo "If you\'ve built your own kernel, please make sure that any"
          echo "custom version numbers are appended to the upstream"
          echo "kernel number with a dash or some other delimiter."

          exit 1
      fi

      # sanity checking for the appropriate kernel on each architecture.
      realarch=`uname -m`
      kernel_ver=`uname -r`

      # convert "armv4l" and similar to just "arm", and "mips64" and similar
      # to just "mips"
      case $realarch in
        arm*) realarch="arm";;
        mips*) realarch="mips";;
      esac

      # The GNU libc requires a >= 2.6.18 kernel (except on m68k)
      if [ "$realarch" != m68k ]
      then
          if linux_compare_versions "$kernel_ver" lt 2.6.18
          then
              echo WARNING: this version of the GNU libc requires kernel version
              echo 2.6.18 or later.  Please upgrade your kernel before installing
              echo glibc.
              kernel26_help

              exit 1
          fi
      fi

      # The GNU libc is now built with --with-kernel= >= 2.4.1 on m68k
      if [ "$realarch" = m68k ]
      then
          if linux_compare_versions "$kernel_ver" lt 2.4.1
          then
              echo WARNING: This version of glibc requires that you be running
              echo kernel version 2.4.1 or later.  Earlier kernels contained
              echo bugs that may render the system unusable if a modern version
              echo of glibc is installed.
              kernel26_help

              exit 1
          fi
      fi

      # From glibc 2.6-3 SPARC V8 support is dropped.
      if [ "$realarch" = sparc ]
      then
          # The process could be run using linux32, check for /proc.
          if [ -f /proc/cpuinfo ]
          then
             case "$(sed '/^type/!d;s/^type.*: //g' /proc/cpuinfo)" in
                 sun4u)
                    # UltraSPARC CPU
                    ;;
                 sun4v)
                    # Niagara CPU
                    ;;
                 *)
                    echo "WARNING: This machine has a SPARC V8 or earlier class processor."
                    echo "Debian lenny and later does not support such old hardware"
                    echo "any longer."
                    exit 1
                    ;;
             esac
          fi
      fi
  elif [ $system = "GNU/kFreeBSD" ] ; then
      kernel_ver=`uname -r`
      if kfreebsd_compare_versions "$kernel_ver" lt 6.0
      then
          echo WARNING: This version of glibc uses UMTX_OP_WAIT and UMTX_OP_WAKE
	    echo syscalls that are not present in the current running kernel. They
	    echo have been added in kFreeBSD 6.0.  Your system should still work,
	    echo but it is recommended to upgrade to a more recent version.
      fi
  fi
fi



exit 0
