#!/usr/bin/env bash
# 10-build-sources.sh - build the analysis tools from the bundled sources.
#
#   build/10-build-sources.sh              # build everything
#   build/10-build-sources.sh fatrace      # build just one
#
# Use this when you may not install RPMs on the target, or when you need a
# newer strace than RHEL 9.6 ships (6.12 vs 7.2 bundled here - newer strace
# decodes more syscalls and has better -y annotation).
# Requires: gcc, make, and for strace/lsof: glibc headers. All offline.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SRC="$ROOT/src"
PREFIX="$ROOT/build"
BIN="$PREFIX/bin"
WORK="$PREFIX/work"
mkdir -p "$BIN" "$WORK"
JOBS="$(nproc 2>/dev/null || echo 2)"
# use the system interpreter, not whatever venv happens to be on PATH
PYTHON="${PYTHON:-/usr/bin/python3}"

log() { printf '\n=== %s\n' "$*"; }

build_strace() {
  log "strace (this takes a few minutes)"
  cd "$WORK"; rm -rf strace-7.2; tar xf "$SRC/strace-7.2.tar.xz"
  cd strace-7.2
  ./configure --prefix="$PREFIX" --enable-mpers=no >configure.log 2>&1 || {
    echo "configure failed, see $PWD/configure.log" >&2; return 1; }
  make -j"$JOBS" >build.log 2>&1 || { echo "build failed, see $PWD/build.log" >&2; return 1; }
  install -m755 src/strace "$BIN/strace"
  "$BIN/strace" --version | head -1
}

build_lsof() {
  log "lsof"
  cd "$WORK"; rm -rf lsof-4.99.7; tar xf "$SRC/lsof-4.99.7.tar.gz"
  cd lsof-4.99.7
  ./configure --prefix="$PREFIX" >configure.log 2>&1 || {
    echo "configure failed, see $PWD/configure.log" >&2; return 1; }
  make -j"$JOBS" >build.log 2>&1 || { echo "build failed, see $PWD/build.log" >&2; return 1; }
  install -m755 "$(find . -name lsof -type f -perm -u+x | head -1)" "$BIN/lsof"
  "$BIN/lsof" -v 2>&1 | head -2
}

build_fatrace() {
  log "fatrace (fanotify - system-wide file access)"
  cd "$WORK"; rm -rf fatrace-0.19.1; tar xf "$SRC/fatrace-0.19.1.tar.gz"
  cd fatrace-0.19.1
  make fatrace >build.log 2>&1 || { echo "build failed, see $PWD/build.log" >&2; return 1; }
  install -m755 fatrace "$BIN/fatrace"
  echo "installed $BIN/fatrace"
}

build_extrace() {
  log "extrace (netlink connector - every exec system-wide)"
  cd "$WORK"; rm -rf extrace-0.9; tar xf "$SRC/extrace-0.9.tar.gz"
  cd extrace-0.9
  make >build.log 2>&1 || { echo "build failed, see $PWD/build.log" >&2; return 1; }
  install -m755 extrace "$BIN/extrace"
  [[ -f pwait ]] && install -m755 pwait "$BIN/pwait"
  echo "installed $BIN/extrace"
}

build_pyelftools() {
  log "pyelftools (scriptable ELF parsing, no compiler needed)"
  cd "$WORK"; rm -rf pyelftools-0.32; tar xf "$SRC/pyelftools-0.32.tar.gz"
  cd pyelftools-0.32
  if "$PYTHON" setup.py install --prefix="$PREFIX" >build.log 2>&1; then
    echo "installed under $PREFIX/lib (for $("$PYTHON" -V 2>&1))"
  else
    echo "setup.py install failed (no setuptools?) - use it straight from the source dir"
  fi
  echo "$PWD" > "$PREFIX/pyelftools.path"
  echo "use with:  PYTHONPATH=$PWD $PYTHON -c 'from elftools.elf.elffile import ELFFile'"
}

case "${1:-all}" in
  strace)     build_strace ;;
  lsof)       build_lsof ;;
  fatrace)    build_fatrace ;;
  extrace)    build_extrace ;;
  pyelftools) build_pyelftools ;;
  all)
    build_fatrace || true
    build_extrace || true
    build_pyelftools || true
    build_strace || true
    build_lsof || true
    ;;
  *) sed -n '2,12p' "$0"; exit 1 ;;
esac

echo
echo "binaries in $BIN:"
ls -l "$BIN" 2>/dev/null || echo "(none built)"
echo
echo "put them first on PATH with:  export PATH=\"$BIN:\$PATH\""
