#!/bin/dash
export PATH=/bin
l=/var/lib/rebase
b=/var/cache/rebase
eb="${b}/rebase_epoch"
db="${l}/dynpath.d"
ub="${l}/user.d"
for d in ${b} ${db} ${ub} ; do
  if [ ! -d ${d} ] ; then
    echo "Directory ${d} does not exist, trying to re-create."
    mkdir -p ${d}
  fi
done

BaseAddress=''
if [ "x86_64" = $( uname -m ) ] ; then
  DefaultBaseAddress=0x400000000
else
  DefaultBaseAddress=0x070000000
fi    

cleardb="no"
rebuild="no"
noaslr="no"
verbose=""
pkgdone="no"
doSuffixes='dll|so|oct|mex'
exeSuffixes='exe'

dynPaths=$( cat ${db}/* | sort -u )
dynLocs=''
for d in ${dynPaths} ; do
  [ -d "${d}" ] && dynLocs="${dynLocs} ${d}"
done

usage () {
  echo "
rebaselst [-h | --help | [ <cmd> | <cmd1> <cmd2> ... <cmdn> ]]

Commands, will be executed in order:
------------------------------------

--verbose
	Run some commands with verbose output.

--rebuild
	Later commands will discard information in cache files and
	rebuild them from scratch.

--no-rebuild
	Keep and use information in cache files.  This is the default,
	can be used to cancel effect of a \"--rebuild\" earlier on the
	command line.

update
	Update all caches.

lst
	Update cache for package installation lists.

user
	Update cache for user defined rebase list(s).

dyn
	Update cache for dynamic language modules and libraries.  This
	currently implies \"--rebuild\" since modules could have been
	installed by the user and the cache would be outdated.

--cleardb
	Remove an existing rebase database before doing a \"rebase\".

--noaslr
	Remove ASLR and TSAware flags from dynamic objects.

rebase
	Rebase with the information in cache files (i.e does not imply
	an \"update\").

peflags
	Set PE flags on executables with the information in cache
	files (i.e does not imply an \"update\").


pkg
	Update first stage cache for package installation lists only.
	This step is implied by \"lst\" and available for debugging
	only.

"
}

check_file () {
  if [ "$2" = "yes" -a  -e $1 ] ; then
    echo "removing $1"
    rm -f $1
  fi
  if [ ! -e "${eb}" ] ; then
      touch -d "@0"                          "${eb}" || \
	  touch -d "1980-01-01 00:00 UTC"    "${eb}" || \
	  touch -d "1999-05-03 07:29:06 UTC" "${eb}" || \
	  touch -d "2002-08-17 07:00 CEST"   "${eb}"
  fi
  if [ ! -e "$1" ] ; then
    echo "creating empty $1"
    touch -r "${eb}"  "$1"
  fi
  if [ "$2" != "nowrite" -a  -e $1 ] ; then
    chmod 644 "$1"
  fi
}

_tr () {
  local IFS s r
  IFS="/-+.,"
  for s in $1; do
    r="${r}_${s}"
  done
  echo "${r}"
}

update_file () {
  local IFS f g k l m
  f="$1"
  g="${f}.old"
  mv -f "${f}" "${g}"
  cat >"${f}" <<EOF
## autogenerated, do not edit!
EOF
    IFS='
'
  for k in $( grep -E '^# rebase_pkg' "${g}" | tr -c '[:alnum:]\n:' '_' ) ; do
    l=${k##*from:_}
    m=$(( ${l-0}+=1 ))
  done
  {
    m=0
    for k in $( grep -vE "^##" "${g}" ) ; do
      if [ -e "${k}" -a "${m}" = "0" ] ; then
    	echo "${k}"
      elif [ "#" = "${k%% rebase_pkg*}" ] ; then
	l=$( _tr "${k##*from: /}" )
	if [ "$(( ${l-0}-=1 ))" = "0" ] ; then
	  m=0
    	  echo "${k}"
	else
	  m=1
    	  echo "#${k}"
	fi
      elif [ ! "##" = "${k%%[^#]*}" ] ; then
    	echo "#${k}"
      fi
    done
  } >>"${f}"
  chmod 444 "${f}"
}

rebase_do () {
  local g
  if [ "$1" = "yes" ] ; then
    rm -f /etc/rebase.db.*
  fi
  shift
  g="${b}/rebase_all"
  echo "Rebasing with list ${g}, built from $@."
  cat $@ | grep -vE '^#' | sort -u >"${g}"
  if [ ! -e "/etc/rebase.db.i386" -a ! -e "/etc/rebase.db.x86_64" ] ; then
    BaseAddress="-b ${DefaultBaseAddress}"
  fi
  rebase ${BaseAddress} ${verbose} -n -s -T "${g}"
  if [ "noaslr" = "yes" ] ; then
    peflags ${verbose} -d0 -t0 -T "${g}"
  fi
  BaseAddress=''
}

peflags_do () {
  local g
  g="${b}/rebase_all_exe"
  echo "Setting PE flags with list ${g}, built from $@."
  cat $@ | grep -vE '^#|ddd\/Ddd\.exe$' | sort -u >"${g}"
  peflags ${verbose} -t1 -T "${g}"
}

rebase_pkg () {
  if [ "${pkgdone}" = "no" ] ; then
    local f g
    g="${b}/rebase_pkg"
    check_file "${g}" ${rebuild}
    echo "Updating package information in ${g}."
    for f in $( find /etc/setup -xdev -type f -name '*.lst.gz' -newer "${g}" ) ; do
      echo "\tfrom ${f}..."
      cat >>"${g}" <<EOF
# rebase_pkg information from: ${f}
EOF
      gzip -d -c "${f}" |
      grep -E "\.(${doSuffixes}|${exeSuffixes})\$" |
      sed -e '/\(cygwin1\|cyglsa.*\)\.dll$/d' \
          -e '/\/\(d\?ash|rebase\|peflags\)\.exe$/d' \
	  -e '/\/octave\//!{/\(mex\|oct\)$/d}' \
	  -e '/gnuplot\/demo\/plugin\/.*\.so/d' \
	  -e '/^usr\/lib\/ocaml\/.*\.so/d' \
	  -e '/sys-root\/mingw/d' \
	  -e 's/^/\//' \
          >>"${g}"
    done
    update_file "${g}"
    pkgdone="yes"
  fi
}

rebase_exe () {
  local f g
  f="${b}/rebase_pkg"
  g="${b}/rebase_exe"
  check_file "${f}" "nowrite"
  check_file "${g}" "no"
  rebase_pkg
  if [ "${f}" -nt "${g}" ] ; then
    echo "Updating rebase information for installed executables in ${g}."
    grep -E "\.(${exeSuffixes})\$" "${f}" >"${g}"
  fi
  update_file "${g}"
}

rebase_lst () {
  local f g
  f="${b}/rebase_pkg"
  rebase_pkg
  g="${b}/rebase_lst"
  check_file "${g}" ${rebuild}
  if [ "${f}" -nt "${g}" ] ; then
    echo "Updating rebase information for installed dynamic objects in ${g}."
    grep -E "\.(${doSuffixes})\$" "${f}" >"${g}"
  fi
  update_file "${g}"
}

rebase_dyn () {
  local f g
  g="${b}/rebase_dyn"
  check_file "${g}" "yes"
  if [ "x" = "${dynLocs:-x}" ] ; then
    touch "${g}"
  else
    echo "Looking for dynamic language modules/libraries in:"
    for d in ${dynLocs} ; do echo "  ${d}" ; done
    echo "Updating rebase information for dynamic language modules/libraries ${g}."
    find ${dynLocs} -xdev -regextype posix-extended -regex ".*\.(${doSuffixes})$" -newer "${g}" >>"${g}"
  fi
  update_file "${g}"
}

rebase_user () {
  local f g
  g="${b}/rebase_user"
  check_file "${g}" ${rebuild}
  echo "Updating rebase information for user-defined dynamic objects ${g}."
  for f in $( find ${ub} -xdev -type f -newer "${g}" ) ; do
    grep -E "\.(${doSuffixes})\$" "${f}" >>"${g}"
  done
  update_file "${g}"
}

rebase_user_exe () {
  local g
  g="${b}/rebase_user_exe"
  check_file "${g}" ${rebuild}
  echo "Updating rebase information for user-defined executables ${g}."
  for f in $( find ${ub} -xdev -type f -newer "${g}" ) ; do
    grep -E "\.(${exeSuffixes})\$" "${f}" >>"${g}"
  done
  update_file "${g}"
}

if [ "$#" = "0" ] ; then
  set -- "--help"
fi
while [ $# -gt 0 ] ; do 
  case "$1" in
    --verbose )
      verbose=--verbose
      ;;
    --cleardb )
      cleardb=yes
      ;;
    --rebuild )
      rebuild=yes
      ;;
    --noaslr )
      noaslr=yes
      ;;
    --no-rebuild )
      rebuild=no
      ;;
    rebase )
      rebase_do "${cleardb}" "${b}/rebase_lst" "${b}/rebase_dyn" "${b}/rebase_user"
      ;;
    peflags )
      peflags_do "${b}/rebase_exe" "${b}/rebase_user_exe"
      ;;
    update )
      shift
      set -- dummy lst dyn user $@
      ;;
    pkg )
      rebase_pkg
      ;;
    lst )
      rebase_lst
      rebase_exe
      ;;
    dyn )
      rebase_dyn
      ;;
    user )
      rebase_user
      rebase_user_exe
      ;;
    -h|--help|* )
      usage
      exit 127
      ;;
  esac
  shift
done
exit 0
