1#!/bin/sh -e 2 3set -euo pipefail 4 5OPTS="bmcstate,bootprogress,chassiskill,chassisoff,chassison,chassisstate,hoststate,\ 6osstate,power,poweroff,poweron,state,status,hostrebootoff,hostrebooton,recoveryoff,recoveryon,\ 7bmcrebootoff, bmcrebooton, listbootblock listlogs showlog" 8 9USAGE="Usage: obmcutil [-h] [--wait] [--verbose] 10 {$OPTS}" 11 12INTERFACE_ROOT=xyz.openbmc_project 13STATE_INTERFACE=$INTERFACE_ROOT.State 14CONTROL_INTERFACE=$INTERFACE_ROOT.Control 15 16OBJECT_ROOT=/xyz/openbmc_project 17STATE_OBJECT=$OBJECT_ROOT/state 18CONTROL_OBJECT=$OBJECT_ROOT/control 19 20HOST_TIMEOUT_TARGET=obmc-host-timeout@0.target 21HOST_CRASH_TARGET=obmc-host-crash@0.target 22 23## NOTE: The following global variables are used only in the run_timeout cmd. 24## By declaring these globally instead of passing them through the 25## intermediary functions, which may not be "best practice", the readability 26## and cleanliness of the code should at least be increased. 27 28# The command passed in to be executed (e.g. poweron/off, status, etc.) 29# This will be be used in some instances of error reporting 30G_ORIG_CMD= 31# The state an interface should be in after executing the requested command. 32G_REQUESTED_STATE= 33# The query to run during a poweron/off or chassison/off to check that 34# the requested state (G_REQUESTED_STATE) of the interface has been reached. 35G_QUERY= 36# Wait the set period of time for state transitions to be successful before 37# continuing on with the program or reporting an error if timeout reached. 38G_WAIT= 39# Print the journal to the console 40G_VERBOSE= 41 42print_help () 43{ 44 echo "$USAGE" 45 echo "" 46 echo "positional arguments:" 47 echo " {$OPTS}" 48 echo "" 49 echo "Examples:" 50 echo "" 51 echo "obmcutil hostrebootoff Disable auto reboot of Host from Quiesce state" 52 echo "obmcutil hostrebooton Enable auto reboot of Host from Quiesce state" 53 echo "" 54 echo "obmcutil bmcrebootoff Disable reboot of BMC" 55 echo "obmcutil bmcrebooton Enable reboot of BMC" 56 echo "" 57 echo "obmcutil recoveryoff Disable handling boot watchdog timeout and host crash" 58 echo " Also, disable BMC and Host auto reboots" 59 echo "" 60 echo "obmcutil recoveryon Enable handling boot watchdog timeout and host crash" 61 echo " Also, enable BMC and Host auto reboots" 62 echo "" 63 echo "obmcutil listbootblock Check for and list any errors blocking the boot" 64 echo " of the system" 65 echo "" 66 echo "obmcutil listlogs List all phosphor-logging entries on the" 67 echo " system" 68 echo "" 69 echo "obmcutil showlog <log> Display details of input log. Format of <log>" 70 echo " should match listlogs output" 71 echo "" 72 echo "optional arguments:" 73 echo " -h, --help show this help message and exit" 74 echo " -w, --wait block until state transition succeeds or fails" 75 echo " -v, --verbose print the journal to stdout if --wait is supplied" 76 exit 0 77} 78 79run_timeout () 80{ 81 local timeout="$1"; shift 82 local cmd="$@" 83 local verbose_child= 84 85 if [ -n "$G_VERBOSE" ]; then 86 journalctl -f & 87 verbose_child=$! 88 fi 89 90 $cmd 91 92 # Run a background query for the transition to the expected state 93 # This will be killed if the transition doesn't succeed within 94 # a timeout period. 95 ( 96 while ! grep -q $G_REQUESTED_STATE <<< $(handle_cmd $G_QUERY) ; do 97 sleep 1 98 done 99 ) & 100 wait_child=$! 101 102 # Could be bad if process is killed before 'timeout' occurs if 103 # transition doesn't succeed. 104 trap -- "" SIGTERM 105 106 # Workaround for lack of 'timeout' command. 107 ( 108 sleep $timeout 109 kill $wait_child 110 ) > /dev/null 2>&1 & 111 112 if ! wait $wait_child; then 113 echo "Unable to confirm '$G_ORIG_CMD' success" \ 114 "within timeout period (${timeout}s)" 115 fi 116 117 if [ -n "$verbose_child" ]; then 118 kill $verbose_child 119 fi 120} 121 122run_cmd () 123{ 124 local cmd="$@"; 125 126 if [ -n "$G_WAIT" ]; then 127 run_timeout $G_WAIT "$cmd" 128 else 129 $cmd 130 fi 131} 132 133set_property () 134{ 135 run_cmd busctl set-property "$@" 136} 137 138get_property () 139{ 140 G_WAIT="" 141 run_cmd busctl get-property "$@" 142} 143 144state_query () 145{ 146 local state=$(get_property "$@" | cut -d '"' -f2) 147 printf "%-20s: %s\n" $4 $state 148} 149 150print_usage_err () 151{ 152 echo "ERROR: $1" >&2 153 echo "$USAGE" 154 exit 1 155} 156 157mask_systemd_target () 158{ 159 target="$@" 160 systemctl mask $target 161} 162 163unmask_systemd_target () 164{ 165 target="$@" 166 systemctl unmask $target 167} 168 169disable_bmc_reboot () 170{ 171 dir="/run/systemd/system/" 172 file="reboot-guard.conf" 173 units=("reboot" "poweroff" "halt") 174 175 for unit in "${units[@]}"; do 176 mkdir -p ${dir}${unit}.target.d 177 echo -e "[Unit]\nRefuseManualStart=yes" >> ${dir}${unit}.target.d/${file} 178 done 179} 180 181enable_bmc_reboot () 182{ 183 dir="/run/systemd/system/" 184 file="reboot-guard.conf" 185 units=("reboot" "poweroff" "halt") 186 187 for unit in "${units[@]}"; do 188 rm -rf ${dir}${unit}.target.d/${file} 189 rm -rf ${dir}${unit}.target.d 190 done 191} 192 193# will write blocking errors to stdout 194check_boot_block_errors () 195{ 196 # array of boot block objects 197 blockArray=() 198 199 # Look for any objects under logging that implement the 200 # xyz.openbmc_project.Logging.ErrorBlocksTransition 201 subtree="$(busctl call xyz.openbmc_project.ObjectMapper \ 202 /xyz/openbmc_project/object_mapper \ 203 xyz.openbmc_project.ObjectMapper \ 204 GetSubTree sias "/xyz/openbmc_project/logging/" 0 1 \ 205 xyz.openbmc_project.Logging.ErrorBlocksTransition)" 206 207 # remove quotation marks 208 subtree="$(echo $subtree | sed 's/\"//g')" 209 210 for entry in $subtree; do 211 if [[ ${entry} =~ "xyz/openbmc_project/logging/block"* ]]; then 212 blockArray+=( $entry ) 213 fi 214 done 215 216 # now find associated error log for each boot block error 217 for berror in "${blockArray[@]}"; do 218 assocs="$(busctl call xyz.openbmc_project.Logging $berror \ 219 org.freedesktop.DBus.Properties Get \ 220 ss xyz.openbmc_project.Association.Definitions Associations)" 221 222 # remove quotation marks 223 assocs="$(echo $assocs | sed 's/\"//g')" 224 225 for entry in $assocs; do 226 if [[ ${entry} =~ "xyz/openbmc_project/logging/entry"* ]]; then 227 echo "Blocking Error: $entry" 228 fi 229 done 230 done 231} 232 233# helper function to check for boot block errors and notify user 234check_and_warn_boot_block() 235{ 236 blockingErrors=$(check_boot_block_errors) 237 if ! [ -z "$blockingErrors" ]; then 238 echo !!!!!!!!!! 239 echo "WARNING! System has blocking errors that will prevent boot" 240 echo "$blockingErrors" 241 echo !!!!!!!!!! 242 fi 243} 244 245# list all phosphor-logging entries 246list_logs() 247{ 248 # Look for any objects under logging that implement the 249 # xyz.openbmc_project.Logging.Entry 250 busctl -j call xyz.openbmc_project.ObjectMapper \ 251 /xyz/openbmc_project/object_mapper \ 252 xyz.openbmc_project.ObjectMapper \ 253 GetSubTreePaths sias "/xyz/openbmc_project/logging/" 0 1 \ 254 xyz.openbmc_project.Logging.Entry 255} 256 257# display input log details 258show_log() 259{ 260 busctl -j call xyz.openbmc_project.Logging \ 261 $1 \ 262 org.freedesktop.DBus.Properties \ 263 GetAll s xyz.openbmc_project.Logging.Entry 264} 265 266handle_cmd () 267{ 268 case "$1" in 269 chassisoff) 270 OBJECT=$STATE_OBJECT/chassis0 271 SERVICE=$(mapper get-service $OBJECT) 272 INTERFACE=$STATE_INTERFACE.Chassis 273 PROPERTY=RequestedPowerTransition 274 VALUE=$INTERFACE.Transition.Off 275 G_REQUESTED_STATE=$INTERFACE.PowerState.Off 276 G_QUERY="chassisstate" 277 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 278 ;; 279 chassison) 280 check_and_warn_boot_block 281 OBJECT=$STATE_OBJECT/chassis0 282 SERVICE=$(mapper get-service $OBJECT) 283 INTERFACE=$STATE_INTERFACE.Chassis 284 PROPERTY=RequestedPowerTransition 285 VALUE=$INTERFACE.Transition.On 286 G_REQUESTED_STATE=$INTERFACE.PowerState.On 287 G_QUERY="chassisstate" 288 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 289 ;; 290 poweroff) 291 OBJECT=$STATE_OBJECT/host0 292 SERVICE=$(mapper get-service $OBJECT) 293 INTERFACE=$STATE_INTERFACE.Host 294 PROPERTY=RequestedHostTransition 295 VALUE=$INTERFACE.Transition.Off 296 G_REQUESTED_STATE=$INTERFACE.HostState.Off 297 G_QUERY="hoststate" 298 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 299 ;; 300 poweron) 301 check_and_warn_boot_block 302 OBJECT=$STATE_OBJECT/host0 303 SERVICE=$(mapper get-service $OBJECT) 304 INTERFACE=$STATE_INTERFACE.Host 305 PROPERTY=RequestedHostTransition 306 VALUE=$INTERFACE.Transition.On 307 G_REQUESTED_STATE=$INTERFACE.HostState.Running 308 G_QUERY="hoststate" 309 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 310 ;; 311 bmcstate) 312 OBJECT=$STATE_OBJECT/bmc0 313 SERVICE=$(mapper get-service $OBJECT) 314 INTERFACE=$STATE_INTERFACE.BMC 315 PROPERTY=CurrentBMCState 316 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 317 ;; 318 chassisstate) 319 OBJECT=$STATE_OBJECT/chassis0 320 SERVICE=$(mapper get-service $OBJECT) 321 INTERFACE=$STATE_INTERFACE.Chassis 322 PROPERTY=CurrentPowerState 323 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 324 ;; 325 hoststate) 326 OBJECT=$STATE_OBJECT/host0 327 SERVICE=$(mapper get-service $OBJECT) 328 INTERFACE=$STATE_INTERFACE.Host 329 PROPERTY=CurrentHostState 330 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 331 ;; 332 osstate) 333 OBJECT=$STATE_OBJECT/host0 334 SERVICE=$(mapper get-service $OBJECT) 335 INTERFACE=$STATE_INTERFACE.OperatingSystem.Status 336 PROPERTY=OperatingSystemState 337 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 338 ;; 339 state|status) 340 for query in bmcstate chassisstate hoststate bootprogress osstate 341 do 342 handle_cmd $query 343 done 344 check_and_warn_boot_block 345 ;; 346 bootprogress) 347 OBJECT=$STATE_OBJECT/host0 348 SERVICE=$(mapper get-service $OBJECT) 349 INTERFACE=$STATE_INTERFACE.Boot.Progress 350 PROPERTY=BootProgress 351 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 352 ;; 353 power) 354 OBJECT=/org/openbmc/control/power0 355 SERVICE=$(mapper get-service $OBJECT) 356 INTERFACE=org.openbmc.control.Power 357 for property in pgood state pgood_timeout 358 do 359 # get_property can potentially return several 360 # different formats of values, so we do the parsing outside 361 # of get_property depending on the query. These queries 362 # return 'i VALUE' formatted strings. 363 STATE=$(get_property $SERVICE $OBJECT $INTERFACE $property \ 364 | sed 's/i[ ^I]*//') 365 printf "%s = %s\n" $property $STATE 366 done 367 ;; 368 chassiskill) 369 /usr/libexec/chassiskill 370 ;; 371 hostrebootoff) 372 OBJECT=$CONTROL_OBJECT/host0/auto_reboot 373 SERVICE=$(mapper get-service $OBJECT) 374 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 375 PROPERTY=AutoReboot 376 VALUE=false 377 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "b" $VALUE 378 ;; 379 hostrebooton) 380 OBJECT=$CONTROL_OBJECT/host0/auto_reboot 381 SERVICE=$(mapper get-service $OBJECT) 382 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 383 PROPERTY=AutoReboot 384 VALUE=true 385 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "b" $VALUE 386 ;; 387 bmcrebootoff) 388 disable_bmc_reboot 389 ;; 390 bmcrebooton) 391 enable_bmc_reboot 392 ;; 393 recoveryoff) 394 handle_cmd hostrebootoff 395 handle_cmd bmcrebootoff 396 mask_systemd_target $HOST_TIMEOUT_TARGET 397 mask_systemd_target $HOST_CRASH_TARGET 398 ;; 399 recoveryon) 400 handle_cmd hostrebooton 401 handle_cmd bmcrebooton 402 unmask_systemd_target $HOST_TIMEOUT_TARGET 403 unmask_systemd_target $HOST_CRASH_TARGET 404 ;; 405 listbootblock) 406 blockingErrors=$(check_boot_block_errors) 407 if [ -z "$blockingErrors" ]; then 408 echo "No blocking errors present" 409 else 410 echo "$blockingErrors" 411 fi 412 ;; 413 listlogs) 414 list_logs 415 ;; 416 showlog) 417 show_log $2 418 ;; 419 *) 420 print_usage_err "Invalid command '$1'" 421 ;; 422 esac 423} 424 425for arg in "$@"; do 426 case $arg in 427 -w|--wait) 428 G_WAIT=30 429 continue 430 ;; 431 -h|--help) 432 print_help 433 ;; 434 -v|--verbose) 435 G_VERBOSE=y 436 ;; 437 -*) 438 print_usage_err "Unknown option: $arg" 439 ;; 440 *) 441 G_ORIG_CMD=$arg 442 # pass all arguments to handle_cmd in case command takes additional 443 # parameters 444 handle_cmd "$@" 445 break 446 ;; 447 esac 448done 449