#!/bin/sh
#-*-sh-*-

#
# Copyright © 2009 CNRS
# Copyright © 2009-2019 Inria.  All rights reserved.
# Copyright © 2009-2012 Université Bordeaux
# See COPYING in top-level directory.
#

HWLOC_top_builddir="@HWLOC_top_builddir@"
prefix="@prefix@"
exec_prefix="@exec_prefix@"
bindir="@bindir@"
localstatedir="@localstatedir@"
runstatedir="@HWLOC_runstatedir@"
# this will be changed into $bindir/lstopo during make install
lstopo="$HWLOC_top_builddir/utils/lstopo/lstopo-no-graphics"

# make sure we use default numeric formats
LANG=C
LC_ALL=C
export LANG LC_ALL

gatherio=0
gatherdmi=0

if [ ! -x "$lstopo" ]
then
    error "Could not find lstopo executable in the install or build dir."
    exit 1
fi

error()
{
    echo $@ 2>&1
}

usage()
{
   echo "$0 [options] <savepath>"
   echo "  Saves the Linux topology files (/sys, /proc, ...) under <savepath>.tar.bz2"
   echo "  and the corresponding lstopo verbose output under <savepath>.output"
   echo "Options:"
   echo "  --io   Gather I/O files (takes much longer and generates much larger tarball)"
   echo "  --dmi  Gather SMBIOS files. Works only when run as root. Requires dmi-sysfs kernel module"
   echo "Example:"
   echo "  $0 /tmp/\$(uname -n)"
}

while [ x`echo "$1" | cut -c1` = x- ] ; do
  case $1 in
  --io) gatherio=1;;
  --dmi) gatherdmi=1;;
  *) echo "Unrecognized option: $1"; usage; exit 1;;
  esac
  shift
done

if test $# -lt 1 -o x$1 = x; then
  usage
  exit 1
fi
name="$1"
basename=`basename "$name"`
dirname=`dirname "$name"`

if [ x$gatherio = x0 ]; then
  echo "I/O files won't be saved (--io not given)."
fi
if [ x$gatherdmi = x0 ]; then
  echo "DMI files won't be saved (--dmi not given)."
fi

echo
echo "*** Note that this tool may be slow on large nodes or when I/O is enabled. ***"
echo

if ! mkdir -p "$dirname" ; then
    error "Failed to create directory $dirname."
    exit 1
fi

if [ ! -w  "$dirname" ] ; then
    echo "$dirname is not writable."
    exit 1
fi

destdir=`mktemp -d`

# Use cat so that we properly get proc/sys files even if their
# file length is wrong
savefile() {
  local dest="$1"
  local file="$2"
  if test -r "$file"; then
    dir=`dirname "$file"`
    mkdir -p "$dest/$dir" 2>/dev/null
    cat "$file" > "$dest/$file" 2>/dev/null
  fi
}

savelink() {
  local dest="$1"
  local file="$2"
  dir=`dirname "$file"`
  mkdir -p "$dest/$dir" 2>/dev/null
  cp -P "$file" "$dest/$file"
}

# Gather the following list of files
cat << EOF | while read -r path ; do savefile "$destdir/$basename" "$path" ; done
/proc/cpuinfo
/proc/meminfo
/proc/mounts
/proc/stat
/proc/self/cpuset
/proc/self/cgroup
EOF

# Get all files from the given directory
# Ignore errors since some files may be missing, and some may be
# restricted to root (but we don't need them).
savedir() {
  local dest="$1"
  local path="$2"
  # gather all directories, including empty ones
  find "$path" -type d 2>/dev/null | while read -r dir ; do
    mkdir -p "$dest/$dir" 2>/dev/null
  done
  # gather all files now
  find "$path" -type f 2>/dev/null | while read -r file ; do
    savefile "$dest" "$file"
  done
  # gather symlinks
  find "$path" -type l 2>/dev/null | while read -r link ; do
    savelink "$dest" "$link"
  done
}

# Gather the following list of directories
cat << EOF | while read -r path ; do savedir "$destdir/$basename" "$path" ; done
/sys/devices/system/cpu/
/sys/devices/system/node/
/sys/bus/cpu/devices/
/sys/bus/node/devices/
/sys/class/dmi/id/
/sys/devices/virtual/dmi/id/
/sys/kernel/mm/hugepages/
/proc/device-tree/cpus/
EOF

# Gather the default /var/run/hwloc (in case it was created by a system-wide hwloc-dump-hwdata)
if test -d /var/run/hwloc; then
  savedir "$destdir/$basename" /var/run/hwloc
fi
# Then, gather what the current hwloc installation could have created in a different $runstatedir
if test "$runstatedir" != "/var/run" -a -d "$runstatedir/hwloc"; then
  savedir "$destdir/$basename" "$runstatedir/hwloc/"
  mkdir -p "$destdir/$basename/var/run"
  ln -sr "$destdir/$basename/$runstatedir/hwloc" "$destdir/$basename/var/run/hwloc.runstatedir"
fi
# And gather what custom $HWLOC_DUMPED_HWDATA_DIR
if test "x$HWLOC_DUMPED_HWDATA_DIR" != x -a -d "$HWLOC_DUMPED_HWDATA_DIR"; then
  savedir "$destdir/$basename" "$HWLOC_DUMPED_HWDATA_DIR"
  mkdir -p "$destdir/$basename/var/run"
  ln -sr "$destdir/$basename/$HWLOC_DUMPED_HWDATA_DIR" "$destdir/$basename/var/run/hwloc.HWLOC_DUMPED_HWDATA_DIR"
fi
# Now link to /var/run/hwloc (used by default in lstopo -i) to something sane if needed
if ! test -d "$destdir/$basename/var/run/hwloc"; then
  if test -e "$destdir/$basename/var/run/hwloc.HWLOC_DUMPED_HWDATA_DIR"; then
    ln -sr "$destdir/$basename/var/run/hwloc.HWLOC_DUMPED_HWDATA_DIR" "$destdir/$basename/var/run/hwloc"
  else if test -e "$destdir/$basename/$runstatedir/hwloc"; then
    ln -sr "$destdir/$basename/var/run/hwloc.runstatedir" "$destdir/$basename/var/run/hwloc"
  fi fi
fi

# Optionally gather I/O directories too
if [ x$gatherio = x1 ]; then
  cat << EOF | while read -r path ; do savedir "$destdir/$basename" "$path" ; done
/sys/bus/pci/devices/
/sys/bus/pci/slots/
/sys/block/
/sys/class/block/
/sys/class/dma/
/sys/class/drm/
/sys/class/infiniband/
/sys/class/net/
/sys/class/mic/
EOF
  ls -d /sys/devices/pci* 2>/dev/null | sed -e 's@$@/@' | while read -r path ; do savedir "$destdir/$basename" "$path" ; done
  ls -d /run/udev/data/b* 2>/dev/null | while read -r path ; do savefile "$destdir/$basename" "$path" ; done
fi

if [ x$gatherdmi = x1 ]; then
  savedir "$destdir/$basename" /sys/firmware/dmi/
fi

# Get an entire mount point, after decoding its path
# we don't support path with \n since it would break in 'find ... | while read ..." above
savemntpnt() {
  local encodedpath=$1
  if echo "$1" | grep "\\012" ; then
    echo "Ignoring mount point whose filename contains an end of line."
    return
  fi
  local path=$(echo "$1" | sed -e 's@\\134@\\@g' -e 's@\\040@ @g' -e 's@\\011@	@g')
  savedir "$destdir/$basename" "${path}/"
}

# Gather cgroup/cpuset mntpnts
cat /proc/mounts | while read -r dummy1 mntpath mnttype mntopts dummy2 ; do
  [ x$mnttype = xcpuset ] && savemntpnt "$mntpath"
  [ x$mnttype = xcgroup ] && echo $mntopts | grep -w cpuset >/dev/null && savemntpnt "$mntpath"
done

# export /proc/hwloc-nofile-info during lstopo (needs HWLOC_DUMP_NOFILE_INFO with HWLOC_THISSYSTEM=1)
export HWLOC_DUMP_NOFILE_INFO="$destdir/$basename/proc/hwloc-nofile-info"
"$lstopo" - >/dev/null
# disable HWLOC_DUMP_NOFILE_INFO for next lstopo invocation
export HWLOC_DUMP_NOFILE_INFO=

# Create the archive and keep the tree in /tmp for testing
( cd "$destdir/" && tar cfj "$basename.tar.bz2" "$basename" )
mv "$destdir/$basename.tar.bz2" "$dirname/$basename.tar.bz2"
echo "Topology files gathered in $dirname/$basename.tar.bz2 and kept in $destdir/$basename/"

# Generate the output as well
# we need "Topology not from this system" in the output so as to make test-topology.sh happy
export HWLOC_THISSYSTEM=0
"$lstopo" - -v > "$dirname/$basename.output"
echo "Expected topology output stored in $dirname/$basename.output"
"$lstopo" -.xml --whole-io --whole-system > "$dirname/$basename.xml"
echo "XML topology stored in in $dirname/$basename.xml"

echo
echo "WARNING: Do not post these files on a public list or website unless you"
echo "WARNING: are sure that no information about this platform is sensitive."

exit 0
