#!/bin/sh -e set -euo pipefail OPTS="bmcstate,bootprogress,chassiskill,chassisoff,chassison,chassisstate,hoststate,\ osstate,power,poweroff,poweron,state,status" USAGE="Usage: obmcutil [-h] [--wait] {$OPTS}" INTERFACE_ROOT=xyz.openbmc_project STATE_INTERFACE=$INTERFACE_ROOT.State OBJECT_ROOT=/xyz/openbmc_project STATE_OBJECT=$OBJECT_ROOT/state ## NOTE: The following global variables are used only in the run_timeout cmd. ## By declaring these globally instead of passing them through the ## intermediary functions, which may not be "best practice", the readability ## and cleanliness of the code should at least be increased. # The command passed in to be executed (e.g. poweron/off, status, etc.) # This will be be used in some instances of error reporting G_ORIG_CMD= # The state an interface should be in after executing the requested command. G_REQUESTED_STATE= # The query to run during a poweron/off or chassison/off to check that # the requested state (G_REQUESTED_STATE) of the interface has been reached. G_QUERY= # Wait the set period of time for state transitions to be successful before # continuing on with the program or reporting an error if timeout reached. G_WAIT= print_help () { echo "$USAGE" echo "" echo "positional arguments:" echo " {$OPTS}" echo "" echo "optional arguments:" echo " -h, --help show this help message and exit" echo " -w, --wait block until state transition succeeds or fails" exit 0 } run_timeout () { local timeout="$1"; shift local cmd="$@" $cmd # Run a background query for the transition to the expected state # This will be killed if the transition doesn't succeed within # a timeout period. ( while ! grep -q $G_REQUESTED_STATE <<< $(handle_cmd $G_QUERY) ; do sleep 1 done ) & child=$! # Could be bad if process is killed before 'timeout' occurs if # transition doesn't succeed. trap -- "" SIGTERM # Workaround for lack of 'timeout' command. ( sleep $timeout kill $child ) > /dev/null 2>&1 & if ! wait $child; then echo "Unable to confirm '$G_ORIG_CMD' success" \ "within timeout period (${timeout}s)" fi } run_cmd () { local cmd="$@"; if [ -n "$G_WAIT" ]; then run_timeout $G_WAIT "$cmd" else $cmd fi } set_property () { run_cmd busctl set-property "$@" } get_property () { G_WAIT="" run_cmd busctl get-property "$@" } state_query () { local state=$(get_property "$@" | cut -d '"' -f2) printf "%-20s: %s\n" $4 $state } print_usage_err () { echo "ERROR: $1" >&2 echo "$USAGE" exit 1 } handle_cmd () { case "$1" in chassisoff) OBJECT=$STATE_OBJECT/chassis0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Chassis PROPERTY=RequestedPowerTransition VALUE=$INTERFACE.Transition.Off G_REQUESTED_STATE=$INTERFACE.PowerState.Off G_QUERY="chassisstate" set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE ;; chassison) OBJECT=$STATE_OBJECT/chassis0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Chassis PROPERTY=RequestedPowerTransition VALUE=$INTERFACE.Transition.On G_REQUESTED_STATE=$INTERFACE.PowerState.On G_QUERY="chassisstate" set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE ;; poweroff) OBJECT=$STATE_OBJECT/host0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Host PROPERTY=RequestedHostTransition VALUE=$INTERFACE.Transition.Off G_REQUESTED_STATE=$INTERFACE.HostState.Off G_QUERY="hoststate" set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE ;; poweron) OBJECT=$STATE_OBJECT/host0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Host PROPERTY=RequestedHostTransition VALUE=$INTERFACE.Transition.On G_REQUESTED_STATE=$INTERFACE.HostState.Running G_QUERY="hoststate" set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE ;; bmcstate) OBJECT=$STATE_OBJECT/bmc0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.BMC PROPERTY=CurrentBMCState state_query $SERVICE $OBJECT $INTERFACE $PROPERTY ;; chassisstate) OBJECT=$STATE_OBJECT/chassis0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Chassis PROPERTY=CurrentPowerState state_query $SERVICE $OBJECT $INTERFACE $PROPERTY ;; hoststate) OBJECT=$STATE_OBJECT/host0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Host PROPERTY=CurrentHostState state_query $SERVICE $OBJECT $INTERFACE $PROPERTY ;; osstate) OBJECT=$STATE_OBJECT/host0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.OperatingSystem.Status PROPERTY=OperatingSystemState state_query $SERVICE $OBJECT $INTERFACE $PROPERTY ;; state|status) for query in bmcstate chassisstate hoststate bootprogress osstate do handle_cmd $query done ;; bootprogress) OBJECT=$STATE_OBJECT/host0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=$STATE_INTERFACE.Boot.Progress PROPERTY=BootProgress state_query $SERVICE $OBJECT $INTERFACE $PROPERTY ;; power) OBJECT=/org/openbmc/control/power0 SERVICE=$(mapper get-service $OBJECT) INTERFACE=org.openbmc.control.Power for property in pgood state pgood_timeout do # get_property can potentially return several # different formats of values, so we do the parsing outside # of get_property depending on the query. These queries # return 'i VALUE' formatted strings. STATE=$(get_property $SERVICE $OBJECT $INTERFACE $property \ | sed 's/i[ ^I]*//') printf "%s = %s\n" $property $STATE done ;; chassiskill) /usr/libexec/chassiskill ;; *) print_usage_err "Invalid command '$1'" ;; esac } for arg in "$@"; do case $arg in -w|--wait) G_WAIT=30 continue ;; -h|--help) print_help ;; -*) print_usage_err "Unknown option: $arg" ;; *) G_ORIG_CMD=$arg handle_cmd $arg break ;; esac done