1#!/bin/sh -e 2 3set -euo pipefail 4 5OPTS="bmcstate,bootprogress,chassiskill,chassisoff,chassison,chassisstate,hoststate,\ 6osstate,power,poweroff,poweron,state,status" 7 8USAGE="Usage: obmcutil [-h] [--wait] [--verbose] 9 {$OPTS}" 10 11INTERFACE_ROOT=xyz.openbmc_project 12STATE_INTERFACE=$INTERFACE_ROOT.State 13 14OBJECT_ROOT=/xyz/openbmc_project 15STATE_OBJECT=$OBJECT_ROOT/state 16 17## NOTE: The following global variables are used only in the run_timeout cmd. 18## By declaring these globally instead of passing them through the 19## intermediary functions, which may not be "best practice", the readability 20## and cleanliness of the code should at least be increased. 21 22# The command passed in to be executed (e.g. poweron/off, status, etc.) 23# This will be be used in some instances of error reporting 24G_ORIG_CMD= 25# The state an interface should be in after executing the requested command. 26G_REQUESTED_STATE= 27# The query to run during a poweron/off or chassison/off to check that 28# the requested state (G_REQUESTED_STATE) of the interface has been reached. 29G_QUERY= 30# Wait the set period of time for state transitions to be successful before 31# continuing on with the program or reporting an error if timeout reached. 32G_WAIT= 33# Print the journal to the console 34G_VERBOSE= 35 36print_help () 37{ 38 echo "$USAGE" 39 echo "" 40 echo "positional arguments:" 41 echo " {$OPTS}" 42 echo "" 43 echo "optional arguments:" 44 echo " -h, --help show this help message and exit" 45 echo " -w, --wait block until state transition succeeds or fails" 46 echo " -v, --verbose print the journal to stdout if --wait is supplied" 47 exit 0 48} 49 50run_timeout () 51{ 52 local timeout="$1"; shift 53 local cmd="$@" 54 local verbose_child= 55 56 if [ -n $G_VERBOSE ]; then 57 journalctl -f & 58 verbose_child=$! 59 fi 60 61 $cmd 62 63 # Run a background query for the transition to the expected state 64 # This will be killed if the transition doesn't succeed within 65 # a timeout period. 66 ( 67 while ! grep -q $G_REQUESTED_STATE <<< $(handle_cmd $G_QUERY) ; do 68 sleep 1 69 done 70 ) & 71 wait_child=$! 72 73 # Could be bad if process is killed before 'timeout' occurs if 74 # transition doesn't succeed. 75 trap -- "" SIGTERM 76 77 # Workaround for lack of 'timeout' command. 78 ( 79 sleep $timeout 80 kill $wait_child 81 ) > /dev/null 2>&1 & 82 83 if ! wait $wait_child; then 84 echo "Unable to confirm '$G_ORIG_CMD' success" \ 85 "within timeout period (${timeout}s)" 86 fi 87 88 if [ -n $verbose_child ]; then 89 kill $verbose_child 90 fi 91} 92 93run_cmd () 94{ 95 local cmd="$@"; 96 97 if [ -n "$G_WAIT" ]; then 98 run_timeout $G_WAIT "$cmd" 99 else 100 $cmd 101 fi 102} 103 104set_property () 105{ 106 run_cmd busctl set-property "$@" 107} 108 109get_property () 110{ 111 G_WAIT="" 112 run_cmd busctl get-property "$@" 113} 114 115state_query () 116{ 117 local state=$(get_property "$@" | cut -d '"' -f2) 118 printf "%-20s: %s\n" $4 $state 119} 120 121print_usage_err () 122{ 123 echo "ERROR: $1" >&2 124 echo "$USAGE" 125 exit 1 126} 127 128handle_cmd () 129{ 130 case "$1" in 131 chassisoff) 132 OBJECT=$STATE_OBJECT/chassis0 133 SERVICE=$(mapper get-service $OBJECT) 134 INTERFACE=$STATE_INTERFACE.Chassis 135 PROPERTY=RequestedPowerTransition 136 VALUE=$INTERFACE.Transition.Off 137 G_REQUESTED_STATE=$INTERFACE.PowerState.Off 138 G_QUERY="chassisstate" 139 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 140 ;; 141 chassison) 142 OBJECT=$STATE_OBJECT/chassis0 143 SERVICE=$(mapper get-service $OBJECT) 144 INTERFACE=$STATE_INTERFACE.Chassis 145 PROPERTY=RequestedPowerTransition 146 VALUE=$INTERFACE.Transition.On 147 G_REQUESTED_STATE=$INTERFACE.PowerState.On 148 G_QUERY="chassisstate" 149 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 150 ;; 151 poweroff) 152 OBJECT=$STATE_OBJECT/host0 153 SERVICE=$(mapper get-service $OBJECT) 154 INTERFACE=$STATE_INTERFACE.Host 155 PROPERTY=RequestedHostTransition 156 VALUE=$INTERFACE.Transition.Off 157 G_REQUESTED_STATE=$INTERFACE.HostState.Off 158 G_QUERY="hoststate" 159 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 160 ;; 161 poweron) 162 OBJECT=$STATE_OBJECT/host0 163 SERVICE=$(mapper get-service $OBJECT) 164 INTERFACE=$STATE_INTERFACE.Host 165 PROPERTY=RequestedHostTransition 166 VALUE=$INTERFACE.Transition.On 167 G_REQUESTED_STATE=$INTERFACE.HostState.Running 168 G_QUERY="hoststate" 169 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 170 ;; 171 bmcstate) 172 OBJECT=$STATE_OBJECT/bmc0 173 SERVICE=$(mapper get-service $OBJECT) 174 INTERFACE=$STATE_INTERFACE.BMC 175 PROPERTY=CurrentBMCState 176 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 177 ;; 178 chassisstate) 179 OBJECT=$STATE_OBJECT/chassis0 180 SERVICE=$(mapper get-service $OBJECT) 181 INTERFACE=$STATE_INTERFACE.Chassis 182 PROPERTY=CurrentPowerState 183 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 184 ;; 185 hoststate) 186 OBJECT=$STATE_OBJECT/host0 187 SERVICE=$(mapper get-service $OBJECT) 188 INTERFACE=$STATE_INTERFACE.Host 189 PROPERTY=CurrentHostState 190 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 191 ;; 192 osstate) 193 OBJECT=$STATE_OBJECT/host0 194 SERVICE=$(mapper get-service $OBJECT) 195 INTERFACE=$STATE_INTERFACE.OperatingSystem.Status 196 PROPERTY=OperatingSystemState 197 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 198 ;; 199 state|status) 200 for query in bmcstate chassisstate hoststate bootprogress osstate 201 do 202 handle_cmd $query 203 done 204 ;; 205 bootprogress) 206 OBJECT=$STATE_OBJECT/host0 207 SERVICE=$(mapper get-service $OBJECT) 208 INTERFACE=$STATE_INTERFACE.Boot.Progress 209 PROPERTY=BootProgress 210 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 211 ;; 212 power) 213 OBJECT=/org/openbmc/control/power0 214 SERVICE=$(mapper get-service $OBJECT) 215 INTERFACE=org.openbmc.control.Power 216 for property in pgood state pgood_timeout 217 do 218 # get_property can potentially return several 219 # different formats of values, so we do the parsing outside 220 # of get_property depending on the query. These queries 221 # return 'i VALUE' formatted strings. 222 STATE=$(get_property $SERVICE $OBJECT $INTERFACE $property \ 223 | sed 's/i[ ^I]*//') 224 printf "%s = %s\n" $property $STATE 225 done 226 ;; 227 chassiskill) 228 /usr/libexec/chassiskill 229 ;; 230 *) 231 print_usage_err "Invalid command '$1'" 232 ;; 233 esac 234} 235 236for arg in "$@"; do 237 case $arg in 238 -w|--wait) 239 G_WAIT=30 240 continue 241 ;; 242 -h|--help) 243 print_help 244 ;; 245 -v|--verbose) 246 G_VERBOSE=y 247 ;; 248 -*) 249 print_usage_err "Unknown option: $arg" 250 ;; 251 *) 252 G_ORIG_CMD=$arg 253 handle_cmd $arg 254 break 255 ;; 256 esac 257done 258