Wake on LAN and remote shutdown

Aus wiki.frank-wulf.de
Zur Navigation springen Zur Suche springen

Shell script /usr/bin/fwremote

#!/bin/bash
#
# Author:  Frank Wulf
# Version: 1.0 (2017-10-02)
#
# This program can be used to start up or shut down a remote computer.
#
# Version history:
# 1.0   2017-10-02   Initial release
#

read_conf() {
  resultvar=$1
  # Read value from configuration file
  val=`confget -O -f $conffile -s $rmt $1`
  if [ "$val" == "" ]; then
    # No value found, set to transferred value
    val=$2
  fi
  eval $resultvar="'$val'"
}

log() {
  if [ "$2" == "E" ]; then
    mtype=":ERROR"
  else
    mtype=""
  fi
  echo "`date +'%Y-%m-%d %H:%M:%S'`: ${1}${mtype}"
}

start() {
  sw_started=0
  # Check if remote computer is online
  if `ping $ipaddr -c1 1>/dev/null 2>&1`; then
    # Check status from last run
    if `get_status "$rmt" "Started"`; then
      log "$rmt has already been started remotely."
      exit 0
    else
      set_status "$rmt" "Online"
      log "$rmt is already online and probably started manually."
      exit 0
    fi
  else
    # Remote computer is down
    if [ "$macaddr" == "" ]; then
      log "Error: No MAC address for $rmt defined."
      exit 1
    fi
    # Determine active ethernet card
    interface=`route|grep '^default'|grep -o '[^ ]*$'`
    # Send Wake on Lan request to remote computer
    etherwake -i $interface $macaddr 1>/dev/null 2>&1
    if [ $? -ne 0 ]; then
      log "Error: Sending Wake on Lan request failed."
      exit 1
    fi
    log "Wake on Lan request has been sent to $rmt."
    log "Waiting for startup (max. $timeout_start seconds) ..."
    # Send one ping request per second to remote computer until
    # a) remote computer answers or
    # b) timeout is reached
    start=`date +%s`
    while [ $(( $(date +%s) - $timeout_start )) -lt $start ]; do
      if `ping $ipaddr -c1 1>/dev/null 2>&1`; then
        log "$rmt has answered to ping request."
        sw_started=1
        break
      else
        sleep 1
      fi
    done
    if [ $sw_started -eq 1 ]; then
      # Remote computer is now online, wait <delay_start> seconds
      # before program will terminate
      set_status "$rmt" "Started"
      log "Waiting $delay_start seconds to end program ..."
      sleep $delay_start
      log "$rmt has been started successfully."
      exit 0
    else
      # Remote computer did not respond within timeout range
      set_status "$rmt" "Offline"
      log "Timeout of $timeout_start seconds reached." "E"
      exit 1
    fi
  fi
}

stop() {
  sw_stopped=0
  sw_stop_allowed=0
  # Check if remote computer is online
  if `ping $ipaddr -c1 1>/dev/null 2>&1`; then
    log "$rmt has answered to ping request."
    if [ "$cmd" == "force-stop" ]; then
      # Command to force shutdown was given
      sw_stop_allowed=1
    else
      # Check status from last run
      if `get_status "$rmt" "Started"`; then
        # Last run ended in "Started"
        if [ "$shutdown" != "no" ]; then
          sw_stop_allowed=1
        fi
      else
        # Last run ended not in "Started", so probably a manual start
        if [ "$shutdown" == "yes" ]; then
          sw_stop_allowed=1
        fi
      fi
    fi
    if [ $sw_stop_allowed -eq 1 ]; then
      # Call remote command to shut down
      net rpc shutdown -f -t 0 -C 'System shutdown' -U $user%$pass -I $ipaddr 1>/dev/null 2>&1
      if [ $? -ne 0 ]; then
        log "Sending shutdown request to $rmt failed, exit code = $?." "E"
        exit 1
      fi
      log "Shutdown request has been sent to $rmt."
      log "Waiting for shutdown (max. $timeout_stop seconds) ..."
      # During the shutdown send one ping request per second until
      # a) remote computer does not answer anymore or
      # b) timeout is reached
      start=`date +%s`
      while [ $(( $(date +%s) - $timeout_stop )) -lt $start ]; do
        if ! `ping $ipaddr -c1 1>/dev/null 2>&1`; then
          log "$rmt has stopped answering to ping request."
          sw_stopped=1
          break
        else
          sleep 1
        fi
      done
      if [ $sw_stopped -eq 1 ]; then
        # Remote computer is now offline, wait <delay_stop" seconds
        # before program will terminate
        set_status "$rmt" "Stopped"
        log "Waiting $delay_stop seconds to end program ..."
        sleep $delay_stop
        log "$rmt has been stopped successfully."
        exit 0
      else
        # Remote computer has still answered the ping requests when
        # timeout has been reached
        set_status "$rmt" "Online"
        log "Timeout of $timeout_stop seconds reached." "E"
        exit 1
       fi
    else
      # Shutdown not allowed
      if [ "$shutdown" == "no" ]; then
        log "Remote shutdown not allowed due to configuration restriction."
      else
        set_status "$rmt" "Online"
        log "Remote shutdown not allowed as $rmt seems to be started manually."
      fi
      exit 0
    fi
  else
    # Remote computer is already offline
    if `get_status "$rmt" "Stopped"`; then
      log "$rmt is already offline and probably stopped remotely."
    else
      set_status "$rmt" "Offline"
      log "$rmt is already offline and probably stopped manually."
    fi
    exit 0
  fi
}

status() {
  # Check status of last run
  if `get_status "$rmt" "Started"`; then
    sw_started=1
    sw_stopped=0
  else
    sw_started=0
    # Last run ended not in "Started", check if it ended in "Stopped"
    if `get_status "$rmt" "Stopped"`; then
      sw_stopped=1
    else
      sw_stopped=0
    fi
  fi
  # Check if remote computer is online
  if `ping $ipaddr -c1 1>/dev/null 2>&1`; then
    if [ $sw_started -eq 1 ]; then
      log "$rmt is online and probably started remotely."
    else
      log "$rmt is online and probably started manually."
    fi
    exit 0
  else
    if [ $sw_stopped -eq 1 ]; then
      log "$rmt is offline and probably stopped remotely."
    else
      log "$rmt is offline and probably stopped manually."
    fi
    # Return with an error so that caller can distinguish between
    # online and offline by checking the exit code
    exit 1
  fi
}

show() {
  if [ "$ipaddr" == "" ]; then
    ipaddr="<missing>"
  fi
  if [ "$macaddr" == "" ]; then
    macaddr="<missing>"
  fi
  if [ "$user" == "" ]; then
    user="<missing>"
  fi
  if [ "$pass" == "" ]; then
    pass="<missing>"
  else
    # Password must not be shown in plain text
    pass="<defined>"
  fi
  if [ "$shutdown" == "" ]; then
    shutdown="<missing>"
  fi

  echo "ipaddr:        $ipaddr"
  echo "macaddr:       $macaddr"
  echo "user:          $user"
  echo "pass:          $pass"
  echo "shutdown:      $shutdown"
  echo "timeout_start: $timeout_start"
  echo "delay_start:   $delay_start"
  echo "timeout_stop:  $timeout_stop"
  echo "delay_stop:    $delay_stop"
}

get_status() {
  if `grep "^$1" $statusfile|grep -q "$2$"`; then
    return 0
  else
    return 1
  fi
}

set_status() {
  grep -v "^$1" $statusfile >$statusfile.tmp
  mv $statusfile.tmp $statusfile
  echo "$1:$2" >>$statusfile
}

# Define configuration file
conffile=/etc/fwremote.conf
# Define status file
statusfile=/var/run/fwremote.state

if [ ! -f $statusfile ]; then
  # Status file does not exist, create an empty one
  >$statusfile
fi

# Setup arguments
cmd=$1
rmt=$2

if [ ! "$rmt" == "" ]; then
  # Read values from configuration file
  read_conf "timeout_start" "0"
  read_conf "timeout_stop" "0"
  read_conf "delay_start" "0"
  read_conf "delay_stop" "0"
  read_conf "shutdown" "auto"
  read_conf "ipaddr" "$rmt"
  read_conf "macaddr" ""
  read_conf "user" ""
  read_conf "pass" ""
fi

case $cmd in
    start)
        start
        ;;
    stop|force-stop)
        stop
        ;;
    status)
        status
        ;;
    show)
        show
        ;;
    *)
        echo "Usage: $0 <command> <remote computer>"
        echo ""
        echo "Commands:"
        echo ""
        echo "    start            Wakes up remote computer"
        echo "    stop             Shuts down remote computer"
        echo "    force-stop       Force shutdown of remote computer"
        echo "    status           Shows status of remote computer"
        echo "    show             Shows configuration for remote computer"
        exit 1
esac

exit 0

Configuration file /etc/fwremote.conf

# During remote start the program sends ping requests to the remote
# computer. If timeout has exceeded and remote system is still offline
# then the script will stop with a FAIL.
timeout_start=60
# During remote shutdown the program sends ping requests to the remote
# computer. If timeout has exceeded and remote system is still online
# then the script will stop with a FAIL.
timeout_stop=60
# During remote start this is a delay of n seconds between remote system
# starts answering ping requests and end of program.
delay_start=30
# During remote shutdown this is a delay of n seconds between remote system
# stops answering ping requests and end of program.
delay_stop=10
# This directive controls the shutdown behaviour
# yes:  Force shutdown if shutdown is requested
# no:   Ignore shutdown request
# auto: Shutdown only if system has been started remotely
shutdown=auto

[wulf-pc]
ipaddr=wulf-pc
macaddr=ac:22:0b:78:b6:59
user=<remote user>
pass=<remote password>
timeout_start=120
timeout_stop=120
delay_start=60
delay_stop=5

[cgefi0067]
ipaddr=cgefi0067
macaddr=ec:f4:bb:20:77:10
user=<remote user>
pass=<remote password>
timeout_start=120
timeout_stop=180
delay_stop=5