#!/bin/sh
# Helper for MTR (MDEV-41704): a throwaway ext4 filesystem + its own mariadbd, and a simulated power cut.
#
#   powercut_fs.sh setup   <basedir>   build fs, initialise db, start server (socket /tmp/pcut41704.sock)
#   powercut_fs.sh sync                sync -f the fs (marks everything before as durable)
#   powercut_fs.sh list_par            print "<name> <size>" of .par files in the test db
#   powercut_fs.sh cut                 power cut: drop all unsynced data, kill -9 the server, unmount
#   powercut_fs.sh remount <basedir>   'reboot' part 1: re-mount what reached the 'disk' (server NOT started)
#   powercut_fs.sh start   <basedir>   start the server again
#   powercut_fs.sh recover <basedir>   remount + start
#   powercut_fs.sh dump    <basedir>   run mariadb-dump of db 'test', print and store its exit code
#
# Env SRV_EXTRA: extra mariadbd options for setup (remembered for later starts),
#   e.g. SRV_EXTRA="--log-bin=binlog --sync-binlog=1"
#   powercut_fs.sh cleanup             remove everything
#
# Uses sudo -n (passwordless or cached). Touches only a /dev/shm image, one loop device,
# dm device 'pcut41704' and /mnt/pcut41704. No real disk is touched.

cmd="$1"; B="${2:-}"; B="${B%/}"
IMG=/dev/shm/pcut41704.img
DM=pcut41704
MNT=/mnt/pcut41704
SECT=$((2*1024*2048))
ERRLOG=/tmp/pcut41704.err
PIDF=/tmp/pcut41704.pid
SOCK=/tmp/pcut41704.sock
LOOPF=/tmp/pcut41704.loop
EXTRAF=/tmp/pcut41704.extra
DUMPRC=/tmp/pcut41704.dumprc
SETSID=$(command -v setsid || true)

[ "$cmd" != setup ] && [ -z "${SRV_EXTRA:-}" ] && [ -r "$EXTRAF" ] && SRV_EXTRA=$(cat "$EXTRAF")
start_srv() {
  $SETSID nohup "$B/bin/mariadbd" --no-defaults --basedir="$B" --datadir="$MNT/data" \
      --socket="$SOCK" --pid-file="$PIDF" --log-error="$ERRLOG" \
      --skip-networking --innodb-buffer-pool-size=64M $SRV_EXTRA </dev/null >/dev/null 2>&1 &
  i=0
  until "$B/bin/mariadb-admin" --no-defaults -uroot --socket="$SOCK" ping >/dev/null 2>&1; do
    i=$((i+1)); [ $i -gt 120 ] && { echo "server did not start; see $ERRLOG" >&2; exit 1; }
    sleep 1
  done
}

do_cleanup() {
  [ -r "$PIDF" ] && kill -9 "$(cat "$PIDF")" 2>/dev/null
  sudo -n umount -l "$MNT" 2>/dev/null
  sudo -n dmsetup remove -f "$DM" 2>/dev/null
  if [ -r "$LOOPF" ]; then sudo -n losetup -d "$(cat "$LOOPF")" 2>/dev/null; fi
  for l in $(losetup -j "$IMG" 2>/dev/null | cut -d: -f1); do sudo -n losetup -d "$l" 2>/dev/null; done
  rm -f "$IMG" "$PIDF" "$SOCK" "$LOOPF" "$EXTRAF" "$DUMPRC"
  sudo -n rmdir "$MNT" 2>/dev/null
  return 0
}

need_basedir() { [ -x "$B/bin/mariadbd" ] || { echo "bad basedir: '$B'" >&2; exit 2; }; }

case "$cmd" in
  setup)
    need_basedir
    do_cleanup                                   # remove leftovers of an aborted run
    truncate -s 2G "$IMG" || exit 1
    LOOP=$(sudo -n losetup -f --show "$IMG") || exit 1
    echo "$LOOP" > "$LOOPF"
    sudo -n dmsetup create "$DM" --table "0 $SECT linear $LOOP 0" || exit 1
    sudo -n mkfs.ext4 -q "/dev/mapper/$DM" || exit 1
    sudo -n mkdir -p "$MNT"
    sudo -n mount -o noatime,commit=3600 "/dev/mapper/$DM" "$MNT" || exit 1
    sudo -n chown "$(id -u):$(id -g)" "$MNT"
    inst=""
    for c in "$B/scripts/mariadb-install-db" "$B/bin/mariadb-install-db"; do [ -x "$c" ] && inst="$c" && break; done
    [ -n "$inst" ] || { echo "mariadb-install-db not found under $B" >&2; exit 2; }
    "$inst" --no-defaults --basedir="$B" --datadir="$MNT/data" \
        --auth-root-authentication-method=normal >/tmp/pcut41704.install.log 2>&1 || exit 2
    printf '%s' "${SRV_EXTRA:-}" > "$EXTRAF"
    start_srv
    echo "fs and server ready"
    ;;
  sync)
    sync -f "$MNT" ;;
  list_par)
    ls -l "$MNT/data/test" | awk '/\.par$/ {print $9, $5}' ;;
  cut)
    LOOP=$(cat "$LOOPF")
    sudo -n dmsetup suspend --noflush --nolockfs "$DM" || exit 1
    sudo -n dmsetup load "$DM" --table "0 $SECT error" || exit 1
    sudo -n dmsetup resume "$DM" || exit 1
    kill -9 "$(cat "$PIDF")" 2>/dev/null
    sleep 1
    sudo -n umount -l "$MNT"
    i=0
    until sudo -n dmsetup remove "$DM" 2>/dev/null; do
      i=$((i+1)); [ $i -gt 20 ] && { echo "dmsetup remove failed" >&2; exit 1; }
      sleep 0.5
    done
    sudo -n losetup -d "$LOOP"
    echo "power cut done"
    ;;
  remount|recover)
    need_basedir
    LOOP=$(sudo -n losetup -f --show "$IMG") || exit 1
    echo "$LOOP" > "$LOOPF"
    sudo -n mount -o noatime "$LOOP" "$MNT" || exit 1
    sudo -n chown -R "$(id -u):$(id -g)" "$MNT" 2>/dev/null
    rm -f "$PIDF" "$SOCK"
    if [ "$cmd" = recover ]; then start_srv; echo "recovered"; else echo "remounted"; fi
    ;;
  start)
    need_basedir
    rm -f "$PIDF" "$SOCK"
    start_srv
    echo "started"
    ;;
  dump)
    need_basedir
    "$B/bin/mariadb-dump" --no-defaults -uroot --socket="$SOCK" test >/dev/null 2>"$DUMPRC.err"
    rc=$?
    echo "$rc" > "$DUMPRC"
    echo "mariadb-dump rc=$rc"
    ;;
  cleanup)
    do_cleanup ;;
  *)
    echo "usage: $0 setup|sync|list_par|cut|remount|start|recover|dump|cleanup [basedir]" >&2; exit 2 ;;
esac
exit 0
