#!/bin/sh
#
# wrapper for linbo_cmd
#
# thomas@linuxmuster.net
# 20180220
# GPL V3
#

ARGS="$@"

[ -z "$ARGS" ] && exit 0

# check for concurrent processes
if ps w | grep linbo_cmd | grep -v grep; then
 echo "Es läuft bereits ein linbo_cmd-Prozess. Breche ab!"
 exit 1
fi

export PATH=/bin:/sbin:/usr/bin:/usr/sbin
SECRETS=/tmp/rsyncd.secrets

# test if variable is an integer
isinteger () {
 [ $# -eq 1 ] || return 1
 case $1 in
 *[!0-9]*|"") return 1;;
           *) return 0;;
 esac
}

islinked(){
 local i
 for i in $(ifconfig | grep ^e | awk '{ print $1 }'); do
  ethtool "$i" | grep -i "link detected" | grep -qi "yes" && return 0
 done
 return 1
}

# get server ip
get_server(){
 server=`grep ^linbo_server /tmp/dhcp.log | awk -F\' '{ print $2 }'`
 if [ -z "$server" ]; then
  islinked && udhcpc
  server=`grep ^serverid /tmp/dhcp.log | awk -F\' '{ print $2 }' | tail -1`
  server_check=`grep -i ^server /start.conf | awk -F\= '{ print $2 }' | awk '{ print $1 }' | tail -1`
  if [ "$server_check" = "$server" ]; then
   touch /tmp/network.ok
  else
   return 1
  fi
 fi
 echo "$server"
}

# get cache device
get_cachedev(){
 cachedev=`grep -i ^cache /start.conf | tail -1 | awk -F\= '{ print $2 }' | awk '{ print $1 }'`
 echo "$cachedev"
}

# get downloadtype
get_downloadtype(){
 downloadtype=`grep -i ^downloadtype /start.conf | tail -1 | awk -F\= '{ print $2 }' | awk '{ print $1 }'`
 [ -z "$downloadtype" ] && downloadtype=rsync
 echo "$downloadtype"
}

# get image names
get_images(){
 local baseimages=`grep -i ^baseimage /start.conf | awk -F\= '{ print $2 }' | awk '{ print $1 }'`
 local diffimages=`grep -i ^image /start.conf | awk -F\= '{ print $2 }' | awk '{ print $1 }'`
 [ -n "$baseimages" -a -n "$diffimages" ] && images="$baseimages $diffimages"
 [ -n "$baseimages" -a -z "$diffimages" ] && images="$baseimages"
 echo "$images"
}

# get baseimage name
get_os(){
 # check for valid start.conf
 if ! grep -qi ^"\[os\]" /start.conf; then
  echo "Error! Keine OS-Definition in start.conf gefunden!"
  return 1
 fi
 local line=""
 local c=0
 local param=""
 local value=""
 osname=""
 baseimage=""
 image=""
 bootdev=""
 rootdev=""
 kernel=""
 initrd=""
 append=""
 while read line; do
  # strip trailing comments
  line="${line%\#*}"
  # strip trailing spaces
  line="$(echo "$line" | sed 's/[ \t]*$//')"
  # skip lines beginning with comment
  [ "${line:0:1}" = "#" ] && continue
  # skip empty lines
  [ -z "$line" ] && continue
  # find [OS] entry at position given in $osnr
  case "$line" in
   \[[Oo][Ss]\])
    let c+=1
    if [ $c -gt $osnr ]; then
     return 0
    else
     continue
    fi
   ;;
  esac
  # parse os definition
  if [ $c -eq $osnr ]; then
   param="$(echo $line | awk -F\= '{ print $1 }' | sed 's/[ \t]*$//')"
   value="$(echo "$line" | sed "s/$param//" | sed "s/^[ \t]*//" | sed "s/^=//" | sed "s/^[ \t]*//" | awk -F\# '{ print $1 }' | sed "s/ *$//g")"
   case "$param" in
    [Nn][Aa][Mm][Ee]) osname="$value" ;;
    [Ii][Mm][Aa][Gg][Ee]) image="$value" ;;
    [Bb][Aa][Ss][Ee][Ii][Mm][Aa][Gg][Ee]) baseimage="$value" ;;
    [Bb][Oo][Oo][Tt]) bootdev="$value" ;;
    [Rr][Oo][Oo][Tt]) rootdev="$value" ;;
    [Kk][Ee][Rr][Nn][Ee][Ll]) kernel="$value" ;;
    [Ii][Nn][Ii][Tt][Rr][Dd]) initrd="$value" ;;
    [Aa][Pp][Pp][Ee][Nn][Dd]) append="$value" ;;
   esac
  fi
 done < /start.conf
} # get_os

# print value of start.conf parameter
stripvalue(){
 local line="$1"
 local ret="$(echo $line | awk -F\= '{ print $2 }')"
 [ -z "$ret" ] && ret="-"
 echo "$ret"
}

# get partition data
get_partitions() {
 # check for valid start.conf
 if ! grep -qi ^"\[partition\]" /start.conf; then
  echo "Error! Keine Partitions-Definition in start.conf gefunden!"
  return 1
 fi
 if ! grep -qi ^"\[os\]" /start.conf; then
  echo "Error! Keine OS-Definition in start.conf gefunden!"
  return 1
 fi
 # define local variables
 local dev=""
 local size=""
 local pid=""
 local fstype=""
 local bootable=""
 local line=""
 partitions=""
 # parse start.conf and store partition definitions in /tmp/partitions
 grep ^[\[DdSsIiBbFf][PpEeIiDdOoSs] /start.conf | tr A-Z a-z | sed 's/ //g' | sed -e 's/#.*$//' | while read line; do
  case "$line" in
   \[partition\]*|\[os\]*)
    if [ -n "$dev" -a -n "$size" -a -n "$pid" ]; then
     [ -z "$bootable" ] && bootable="-"
     [ -z "$fstype" ] && fstype="-"
     partitions="$partitions $dev $size $pid $bootable $fstype"
     echo "$partitions" > /tmp/partitions
    fi
    [ "$line" = "\[os\]" ] && break
    dev=""; size=""; pid=""; bootable=""; fstype=""
   ;;
   dev=*) dev="$(stripvalue "$line")" ;;
   size=*)
    size="$(stripvalue "$line")"
    isinteger "$size" || size=0
   ;;
   id=*) pid="$(stripvalue "$line")" ;;
   bootable=*)
    bootable="$(stripvalue "$line")"
    [ "$bootable" = "yes" ] && bootable="bootable"
   ;;
   fstype=*) fstype="$(stripvalue "$line")" ;;
   *) ;;
  esac
 done
 partitions="$(cat /tmp/partitions)"
} # get_partitions

# get partitionlabel from start.conf
# partlabel_startconf partition
partlabel_startconf(){
  [ -s /start.conf ] || return
  local part="$1"
  local line=""
  local dev=""
  local label=""
  grep -i ^[dl][ea][vb] /start.conf | awk -F\= '{ print $1 " " $2 }' | awk '{ print $1 " " $2 }' | while read line; do
    echo "$line" | grep -qi ^dev && dev="$(echo "$line" | awk '{ print $2 }')"
    echo "$line" | grep -qi ^label && label="$(echo "$line" | awk '{ print $2 }')"
    if [ -n "$dev" -a -n "$label" ]; then
      if [ "$dev" = "$part" ]; then
        echo "$label"
        return
      fi
      dev="" ; label=""
    fi
  done
}

# print_partlabel partition
print_partlabel(){
  [ -e /dev/disk/by-label ] || return
  local label="$(partlabel_startconf "$1")"
  [ -z "$label" ] && return
  ls -l /dev/disk/by-label/ | grep -qw "$label" && echo "$label"
}

# format a specific partition
format_partition(){
 local pos=$((((1))+$(($partnr-1))*((5))))
 local dev="$(echo $partitions | cut -d" " -f$pos)"
 local label="$(print_partlabel "$dev")"
 pos=$(($partnr*5))
 local fstype="$(echo $partitions | cut -d" " -f$pos)"
 local fcmd=""
 if [ -n "$label" ]; then
  case "$fstype" in
   swap|ext2|ext3|ext4|[Nn][Tt][Ff][Ss]*) label="-L $label" ;;
   reiserfs) label="-l $label" ;;
   *[Ff][Aa][Tt]*) label="-n $label" ;;
   *) ;;
  esac
 fi
 case "$fstype" in
  [Ss][Ww][Aa][Pp]) fcmd="mkswap $label $dev" ;;
  [Rr][Ee][Ii][Ss][Ee][Rr][Ff][Ss]) fcmd="mkreiserfs $label -f -f $dev" ;;
  [Ee][Xx][Tt][234]) fcmd="mkfs.$fstype -F $label $dev" ;;
  [Nn][Tt][Ff][Ss]) fcmd="mkfs.ntfs $label -Q $dev" ;;
  *[Ff][Aa][Tt]*) fcmd="mkdosfs $label -F 32 $dev" ;;
  *) echo "Unbekanntes Dateisystem: $fstype!"
     return 1 ;;
 esac
 if [ -n "$fcmd" ]; then
  # test if device is present after partitioning, if not wait 3 secs
  if [ ! -b "$dev" ]; then
   echo "Partition $dev ist noch nicht bereit ... Warte 3 Sekunden ..."
   sleep 3
  fi
  # test again, abort if device is not there
  if [ ! -b "$dev" ]; then
   echo "Partition $dev existiert nicht ... Breche ab!"
   return 1
  fi
  if ! $fcmd; then
   echo "Fehler beim Formatieren von $dev ... Breche ab!"
   return 1
  fi
 fi
}

# get rsync user and password
get_passwd(){
 [ -s "$SECRETS" ] || return 1
 user=linbo
 password="$(grep ^"$user:" "$SECRETS" | awk -F\: '{ print $2 }')"
}

# creates image description
create_desc(){
 local image="$1"
 linbo_cmd mountcache "$cachedev" -w
 cat /proc/mounts | grep -q /cache || return 1
 if [ -n "$msg" ]; then
  msg="$(date): $msg"
 else
  msg="$(date): $image created by linbo_wrapper."
 fi
 echo "$msg" > /cache/msg.tmp
 [ -s "/cache/$image.desc" ] && cat "/cache/$image.desc" >> /cache/msg.tmp
 mv /cache/msg.tmp "/cache/$image.desc"
}

# print help
help(){
 echo
 echo "Benutzung: `basename $0` <command1 command2 ...>"
 echo
 echo "`basename $0` erlaubt die einfache Benutzung von linbo_cmd auf der Kommandozeile."
 echo "Es liest die start.conf und setzt die entsprechenden Befehle zusammen."
 echo
 echo "Verfügbare Befehle sind:"
 echo
 echo "partition                : Schreibe die Partitionstabelle."
 echo "label                    : Versieht alle Partitionen mit Label, die in der"
 echo "                           start.conf-Datei definiert und formatiert sind."
 echo "format                   : Schreibt die Partitionstabelle und formatiert"
 echo "                           alle Partitionen."
 echo "format:<#>               : Schreibt die Partitionstabelle und formatiert"
 echo "                           nur Partition Nr. <#>."
 echo "initcache:<dltype>       : Aktualisiert den lokalen Cache. <dltype> ist"
 echo "                           rsync, multicast oder torrent."
 echo "                           Wenn <dltype> nicht angegeben ist wird er aus der"
 echo "                           start.conf gelesen."
 echo "sync:<#>                 : Synchronisiert das Betriebssystem auf Position Nr. <#>."
 echo "start:<#>                : Startet das Betriebssystem auf Position Nr. <#>."
 echo "create_cloop:<#>:<\"msg\"> : Erstellt ein CLOOP-image von Betriebssystem Nr. <#>."
 echo "create_rsync:<#>:<\"msg\"> : Erstellt ein RSYNC-image von Betriebssystem Nr. <#>."
 echo "upload_cloop:<#>         : Lädt das CLOOP-image von Betriebssystem Nr. <#> hoch."
 echo "upload_rsync:<#>         : Lädt das RSYNC-image von Betriebssystem Nr. <#> hoch."
 echo "update                   : Aktualisiere LINBOs Kernel/RAM-Disk und installiere GRUB."
 echo "reboot                   : Client neustarten."
 echo "halt                     : Client herunterfahren."
 echo "help                     : Zeigt diese Hilfe."
 echo
 echo "<\"msg\"> ist ein optionaler Image-Kommentar."
 echo "Die Positions-Nummern beziehen sich auf die Position in der start.conf."
 echo "Die Befehle werden in der Reihenfolge ausgeführt, in der sie übergeben werden."
 echo "Der upload-Befehl erwartet eine Datei /tmp/rsyncd.secrets mit RSYNC-Anmeldedaten"
 echo "in der Form: <Benutzername>:<Passwort>"
 echo
 exit
}

case "$@" in *help*) help ;; esac

# process command line args
while [ "$#" -gt "0" ]; do

 cmd=`echo "$1" | awk -F\: '{ print $1 }'`
 param=`echo "$1" | awk -F\: '{ print $2 }'`
 msg=`echo "$1" | awk -F\: '{ print $3 }'`
 customimage=`echo "$1" | awk -F\: '{ print $4 }'`

 # do not print linbo password
 echo "Befehl      : $cmd"
 [ -n "$param" -a "$cmd" != "linbo" ] && echo "Parameter    : $param"
 [ -n "$msg" ] && echo "Kommentar      : $msg"
 [ -n "$customimage" ] && echo "Benutzerdefiniertes Image : $customimage"

 case "$cmd" in

  linbo)
   echo "${cmd}:${param}" > "$SECRETS"
   ;;

  partition)
   get_partitions
   [ -n "$partitions" ] && linbo_cmd partition_noformat $partitions
  ;;

  format)
   get_partitions
   if [ -n "$partitions" ]; then
    if [ -z "$param" ]; then
     linbo_cmd partition $partitions
    else
     partnr="$param"
     if isinteger "$partnr" && [ $partnr -gt 0 ]; then
      format_partition
     fi
    fi
   fi
  ;;

  initcache)
   [ -z "$server" ] && get_server
   [ -z "$server" ] && return 1
   [ -z "$cachedev" ] && get_cachedev
   if [ "$param" = "rsync" -o "$param" = "multicast" -o "$param" = "torrent" ]; then
    downloadtype="$param"
   else
    get_downloadtype
   fi
   get_images
   if [ -n "$server" -a -n "$cachedev" -a -n "$images" ]; then
    linbo_cmd initcache $server $cachedev $downloadtype $images
   else
    echo "Fehlgeschlagen! Einer oder mehrere benötigte Parameter fehlen!"
    echo "Initcache-Befehl war: linbo_cmd initcache $server $cachedev $downloadtype $images"
   fi
  ;;

  create_cloop)
   osnr="$param"
   if ! isinteger "$osnr"; then
    echo "$osnr ist keine Zahl!"
    shift
    continue
   fi
   get_os
   echo "Erstelle $baseimage von $osname ..."
   [ -z "$cachedev" ] && get_cachedev
   [ -n "$customimage" ] && baseimage="$customimage"
   if [ -n "$cachedev" -a -n "$baseimage" -a -n "$bootdev" -a -n "$rootdev" -a -n "$kernel" ]; then
    create_desc "$baseimage"
    linbo_cmd create "$cachedev" "$baseimage" "" "$bootdev" "$rootdev" "$kernel" "$initrd"
   else
    echo "Fehlgeschlagen! Einer oder mehrere benötigte Parameter fehlen!"
    echo "Create-Befehl war: linbo_cmd create $cachedev $baseimage $bootdev $rootdev $kernel $initrd"
   fi
  ;;

  upload_cloop)
   osnr="$param"
   if ! isinteger "$osnr"; then
    echo "$osnr ist keine Zahl!"
    shift
    continue
   fi
   get_os
   get_passwd
   [ -z "$server" ] && get_server
   [ -z "$server" ] && return 1
   echo "Lade $baseimage zu $server hoch ..."
   [ -z "$cachedev" ] && get_cachedev
   [ -n "$customimage" ] && baseimage="$customimage"
   if [ -n "$server" -a -n "$user" -a -n "$password" -a -n "$cachedev" -a -n "$baseimage" ]; then
    linbo_cmd upload "$server" "$user" "$password" "$cachedev" "$baseimage"
   else
    echo "Fehlgeschlagen! Einer oder mehrere benötigte Parameter fehlen!"
    echo "Upload-Befehl war: linbo_cmd upload $server $user $password $cachedev $baseimage"
   fi
  ;;

  create_rsync)
   osnr="$param"
   if ! isinteger "$osnr"; then
    echo "$osnr ist keine Zahl!"
    shift
    continue
   fi
   get_os
   echo "Erstelle $image von $osname ..."
   [ -z "$cachedev" ] && get_cachedev
   [ -n "$customimage" ] && image="$customimage"
   if [ -n "$cachedev" -a -n "$image" -a -n "$baseimage" -a -n "$bootdev" -a -n "$rootdev" -a -n "$kernel" ]; then
    create_desc "$image"
    linbo_cmd create "$cachedev" "$image" "$baseimage" "$bootdev" "$rootdev" "$kernel" "$initrd"
   else
    echo "Fehlgeschlagen! Einer oder mehrere benötigte Parameter fehlen!"
    echo "Create-Befehl war: linbo_cmd create $cachedev $image $bootdev $rootdev $kernel $initrd"
   fi
  ;;

  upload_rsync)
   osnr="$param"
   if ! isinteger "$osnr"; then
    echo "$osnr is not an integer!"
    shift
    continue
   fi
   get_os
   get_passwd
   echo "Uploading $image to $server ..."
   [ -z "$server" ] && get_server
   [ -z "$server" ] && return 1
   [ -z "$cachedev" ] && get_cachedev
   [ -n "$customimage" ] && image="$customimage"
   if [ -n "$server" -a -n "$user" -a -n "$password" -a -n "$cachedev" -a -n "$image" ]; then
    linbo_cmd upload "$server" "$user" "$password" "$cachedev" "$image"
   else
    echo "Failed! One or more necessary parameters are missing!"
    echo "Upload command was: linbo_cmd upload $server $user $password $cachedev $image"
   fi
  ;;

  sync)
   osnr="$param"
   if ! isinteger "$osnr"; then
    echo "$osnr is not an integer!"
    shift
    continue
   fi
   get_os
   echo "Syncing $osname ..."
   [ -z "$server" ] && get_server
   [ -z "$server" ] && server="offline"
   [ -z "$cachedev" ] && get_cachedev
   if [ -n "$server" -a -n "$cachedev" -a -n "$baseimage" -a -n "$bootdev" -a -n "$rootdev" -a -n "$kernel" ]; then
    linbo_cmd synconly "$server" "$cachedev" "$baseimage" "$image" "$bootdev" "$rootdev" "$kernel" "$initrd" "$append"
   else
    echo "Failed! One or more necessary parameters are missing!"
    echo "Sync command was: linbo_cmd synconly $server $cachedev $baseimage $image $bootdev $rootdev $kernel $initrd $append"
   fi
  ;;

  start)
   osnr="$param"
   if ! isinteger "$osnr"; then
    echo "$osnr ist keine Zahl!"
    shift
    continue
   fi
   get_os
   echo "Starte $osname ..."
   [ -z "$cachedev" ] && get_cachedev
   if [ -n "$bootdev" -a -n "$rootdev" -a -n "$kernel" -a -n "$cachedev" ]; then
    linbo_cmd start "$bootdev" "$rootdev" "$kernel" "$initrd" "$append" "$cachedev"
   else
    echo "Fehlgeschlagen! Einer oder mehrere benötigte Parameter fehlen!"
    echo "Start-Befehl war: linbo_cmd start $bootdev $rootdev $kernel $initrd $append $cachedev"
    exit 1
   fi
  ;;

  update)
   echo "Aktualisiere LINBOs Kernel/RAM-Disk und installiere GRUB ..."
   [ -z "$server" ] && get_server
   [ -z "$cachedev" ] && get_cachedev
   if [ -n "$server" -a -n "$cachedev" ]; then
    linbo_cmd update "$server" "$cachedev"
   else
    echo "Fehlgeschlagen! Einer oder mehrere benötigte Parameter fehlen!"
    echo "Update-Befehl war: linbo_cmd update $server $cachedev"
    exit 1
   fi
  ;;

  label) linbo_cmd label ;;

  reboot) /sbin/reboot ;;

  halt|poweroff) /sbin/poweroff ;;

 esac

 shift

done

exit 0
