1#!/bin/bash -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 deletelogs, stopofftargets" 8 9USAGE="Usage: obmcutil [-h] [--wait] [--verbose] [--id=<INSTANCE_ID>] 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# Instance id, default 0 42G_INSTANCE_ID="0" 43 44function print_help() 45{ 46 echo "$USAGE" 47 echo "" 48 echo "positional arguments:" 49 echo " {$OPTS}" 50 echo "" 51 echo "Examples:" 52 echo "" 53 echo "obmcutil hostrebootoff Disable auto reboot of Host from Quiesce state" 54 echo "obmcutil hostrebootoffonetime Disable auto reboot of Host from" 55 echo " Quiesce state for a single boot" 56 echo "obmcutil hostrebooton Enable auto reboot of Host from Quiesce state" 57 echo "" 58 echo "obmcutil bmcrebootoff Disable reboot of BMC" 59 echo "obmcutil bmcrebooton Enable reboot of BMC" 60 echo "" 61 echo "obmcutil recoveryoff Disable handling boot watchdog timeout and host crash" 62 echo " Also, disable BMC and Host auto reboots" 63 echo "" 64 echo "obmcutil recoveryon Enable handling boot watchdog timeout and host crash" 65 echo " Also, enable BMC and Host auto reboots" 66 echo "" 67 echo "obmcutil recoverystatus Display the status of handling boot watchdog timeout and host crash" 68 echo " and also the status of BMC and Host auto reboots setting" 69 echo "" 70 echo "obmcutil listbootblock Check for and list any errors blocking the boot" 71 echo " of the system" 72 echo "" 73 echo "obmcutil listlogs List all phosphor-logging entries on the" 74 echo " system" 75 echo "" 76 echo "obmcutil showlog <log> Display details of input log. Format of <log>" 77 echo " should match listlogs output" 78 echo "" 79 echo "obmcutil deletelogs Delete all phosphor-logging entries from" 80 echo " system" 81 echo "obmcutil stopofftargets Manually stop all obmc targets in power off" 82 echo " path" 83 echo "" 84 echo "optional arguments (must precede the positional options above):" 85 echo " -h, --help show this help message and exit" 86 echo " -w, --wait block until state transition succeeds or fails" 87 echo " -v, --verbose print the journal to stdout if --wait is supplied" 88 echo " -i, -id instance id, default 0" 89 exit 0 90} 91 92function run_timeout() 93{ 94 local timeout="$1"; shift 95 local cmd="$*" 96 local verbose_child= 97 98 if [ -n "$G_VERBOSE" ]; then 99 journalctl -f & 100 verbose_child=$! 101 fi 102 103 $cmd 104 105 # Run a background query for the transition to the expected state 106 # This will be killed if the transition doesn't succeed within 107 # a timeout period. 108 ( 109 while ! grep -q "$G_REQUESTED_STATE" <<< "$(handle_cmd "$G_QUERY")" ; do 110 sleep 1 111 done 112 ) & 113 wait_child=$! 114 115 # Could be bad if process is killed before 'timeout' occurs if 116 # transition doesn't succeed. 117 trap -- "" SIGTERM 118 119 # Workaround for lack of 'timeout' command. 120 ( 121 sleep "$timeout" 122 kill $wait_child 123 ) > /dev/null 2>&1 & 124 125 if ! wait $wait_child; then 126 echo "Unable to confirm '$G_ORIG_CMD' success" \ 127 "within timeout period (${timeout}s)" 128 fi 129 130 if [ -n "$verbose_child" ]; then 131 kill $verbose_child 132 fi 133} 134 135function run_cmd() 136{ 137 local cmd="$*"; 138 139 if [ -n "$G_WAIT" ]; then 140 run_timeout "$G_WAIT" "$cmd" 141 else 142 $cmd 143 fi 144} 145 146function set_property() 147{ 148 run_cmd busctl set-property "$@" 149} 150 151function get_property() 152{ 153 G_WAIT="" 154 run_cmd busctl get-property "$@" 155} 156 157function state_query() 158{ 159 local state 160 state=$(get_property "$@" | cut -d '"' -f2) 161 printf "%-20s: %s\n" "$4" "$state" 162} 163 164function print_usage_err() 165{ 166 echo "ERROR: $1" >&2 167 echo "$USAGE" 168 exit 1 169} 170 171function mask_systemd_target() 172{ 173 target="$*" 174 systemctl mask "$target" 175} 176 177function unmask_systemd_target() 178{ 179 target="$*" 180 systemctl unmask "$target" 181} 182 183function get_systemd_target_state() 184{ 185 target="$*" 186 enabled_state=$(systemctl is-enabled "$target") 187 echo "$enabled_state" 188} 189 190function disable_bmc_reboot() 191{ 192 dir="/run/systemd/system/" 193 file="reboot-guard.conf" 194 units=("reboot" "poweroff" "halt") 195 196 for unit in "${units[@]}"; do 197 mkdir -p "${dir}${unit}.target.d" 198 echo -e "[Unit]\nRefuseManualStart=yes" >> "${dir}${unit}.target.d/${file}" 199 done 200} 201 202function enable_bmc_reboot() 203{ 204 dir="/run/systemd/system/" 205 file="reboot-guard.conf" 206 units=("reboot" "poweroff" "halt") 207 208 for unit in "${units[@]}"; do 209 rm -rf "${dir}${unit}.target.d/${file}" 210 rm -rf "${dir}${unit}.target.d" 211 done 212} 213 214function get_bmc_reboot_status() 215{ 216 dir="/run/systemd/system/" 217 file="reboot-guard.conf" 218 units=("reboot" "poweroff" "halt") 219 220 # if file do 221 for unit in "${units[@]}"; do 222 if [ -e "${dir}${unit}.target.d/${file}" ]; then 223 echo "off" 224 return 0 225 fi 226 done 227 echo "on" 228 return 0 229} 230 231function get_host_reboot_status() 232{ 233 OBJECT=$CONTROL_OBJECT/host$G_INSTANCE_ID/auto_reboot 234 SERVICE=$(mapper get-service "$OBJECT") 235 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 236 PROPERTY=AutoReboot 237 output="$(get_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY)" 238 echo "${output//b /}" 239} 240 241# will write blocking errors to stdout 242function check_boot_block_errors() 243{ 244 # array of boot block objects 245 blockArray=() 246 247 # Look for any objects under logging that implement the 248 # xyz.openbmc_project.Logging.ErrorBlocksTransition 249 subtree="$(busctl call xyz.openbmc_project.ObjectMapper \ 250 /xyz/openbmc_project/object_mapper \ 251 xyz.openbmc_project.ObjectMapper \ 252 GetSubTree sias "/xyz/openbmc_project/logging/" 0 1 \ 253 xyz.openbmc_project.Logging.ErrorBlocksTransition)" 254 255 # remove quotation marks 256 # shellcheck disable=SC2001 257 subtree="$(echo "$subtree" | sed 's/\"//g')" 258 259 for entry in $subtree; do 260 if [[ ${entry} =~ "xyz/openbmc_project/logging/block"* ]]; then 261 blockArray+=( "$entry" ) 262 fi 263 done 264 265 # now find associated error log for each boot block error 266 for berror in "${blockArray[@]}"; do 267 assocs="$(busctl call xyz.openbmc_project.Logging "$berror" \ 268 org.freedesktop.DBus.Properties Get \ 269 ss xyz.openbmc_project.Association.Definitions Associations)" 270 271 # remove quotation marks 272 # shellcheck disable=SC2001 273 assocs="$(echo "$assocs" | sed 's/\"//g')" 274 275 for entry in $assocs; do 276 if [[ ${entry} =~ "xyz/openbmc_project/logging/entry"* ]]; then 277 echo "Blocking Error: $entry" 278 fi 279 done 280 done 281} 282 283# helper function to check for boot block errors and notify user 284function check_and_warn_boot_block() 285{ 286 blockingErrors=$(check_boot_block_errors) 287 if [ -n "$blockingErrors" ]; then 288 echo !!!!!!!!!! 289 echo "WARNING! System has blocking errors that will prevent boot" 290 echo "$blockingErrors" 291 echo !!!!!!!!!! 292 fi 293} 294 295# list all phosphor-logging entries 296function list_logs() 297{ 298 # Look for any objects under logging that implement the 299 # xyz.openbmc_project.Logging.Entry 300 busctl -j call xyz.openbmc_project.ObjectMapper \ 301 /xyz/openbmc_project/object_mapper \ 302 xyz.openbmc_project.ObjectMapper \ 303 GetSubTreePaths sias "/xyz/openbmc_project/logging/" 0 1 \ 304 xyz.openbmc_project.Logging.Entry 305} 306 307# display input log details 308function show_log() 309{ 310 busctl -j call xyz.openbmc_project.Logging \ 311 "$1" \ 312 org.freedesktop.DBus.Properties \ 313 GetAll s xyz.openbmc_project.Logging.Entry 314} 315 316# delete all phosphor-logging entries 317function delete_logs() 318{ 319 busctl call xyz.openbmc_project.Logging \ 320 /xyz/openbmc_project/logging \ 321 xyz.openbmc_project.Collection.DeleteAll DeleteAll 322} 323 324# stop all targets associated with powering off a system 325function stop_off_targets() 326{ 327 systemctl stop \ 328 obmc-chassis-powered-off@0.target \ 329 obmc-host-stop-pre@0.target \ 330 obmc-host-stopped@0.target \ 331 obmc-host-stopping@0.target \ 332 obmc-power-off@0.target \ 333 obmc-power-stop-pre@0.target \ 334 obmc-power-stop@0.target 335} 336 337function handle_cmd() 338{ 339 case "$1" in 340 chassisoff) 341 OBJECT=$STATE_OBJECT/chassis$G_INSTANCE_ID 342 SERVICE=$(mapper get-service "$OBJECT") 343 INTERFACE=$STATE_INTERFACE.Chassis 344 PROPERTY=RequestedPowerTransition 345 VALUE=$INTERFACE.Transition.Off 346 G_REQUESTED_STATE=$INTERFACE.PowerState.Off 347 G_QUERY="chassisstate" 348 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "s" $VALUE 349 ;; 350 chassison) 351 check_and_warn_boot_block 352 OBJECT=$STATE_OBJECT/chassis$G_INSTANCE_ID 353 SERVICE=$(mapper get-service "$OBJECT") 354 INTERFACE=$STATE_INTERFACE.Chassis 355 PROPERTY=RequestedPowerTransition 356 VALUE=$INTERFACE.Transition.On 357 G_REQUESTED_STATE=$INTERFACE.PowerState.On 358 G_QUERY="chassisstate" 359 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "s" $VALUE 360 ;; 361 poweroff) 362 OBJECT=$STATE_OBJECT/host$G_INSTANCE_ID 363 SERVICE=$(mapper get-service "$OBJECT") 364 INTERFACE=$STATE_INTERFACE.Host 365 PROPERTY=RequestedHostTransition 366 VALUE=$INTERFACE.Transition.Off 367 G_REQUESTED_STATE=$INTERFACE.HostState.Off 368 G_QUERY="hoststate" 369 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "s" $VALUE 370 ;; 371 poweron) 372 check_and_warn_boot_block 373 OBJECT=$STATE_OBJECT/host$G_INSTANCE_ID 374 SERVICE=$(mapper get-service "$OBJECT") 375 INTERFACE=$STATE_INTERFACE.Host 376 PROPERTY=RequestedHostTransition 377 VALUE=$INTERFACE.Transition.On 378 G_REQUESTED_STATE=$INTERFACE.HostState.Running 379 G_QUERY="hoststate" 380 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "s" $VALUE 381 ;; 382 bmcstate) 383 OBJECT=$STATE_OBJECT/bmc0 384 SERVICE=$(mapper get-service $OBJECT) 385 INTERFACE=$STATE_INTERFACE.BMC 386 PROPERTY=CurrentBMCState 387 state_query "$SERVICE" $OBJECT $INTERFACE $PROPERTY 388 ;; 389 chassisstate) 390 OBJECT=$STATE_OBJECT/chassis$G_INSTANCE_ID 391 SERVICE=$(mapper get-service "$OBJECT") 392 INTERFACE=$STATE_INTERFACE.Chassis 393 PROPERTY=CurrentPowerState 394 state_query "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY 395 ;; 396 hoststate) 397 OBJECT=$STATE_OBJECT/host$G_INSTANCE_ID 398 SERVICE=$(mapper get-service "$OBJECT") 399 INTERFACE=$STATE_INTERFACE.Host 400 PROPERTY=CurrentHostState 401 state_query "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY 402 ;; 403 osstate) 404 OBJECT=$STATE_OBJECT/host$G_INSTANCE_ID 405 SERVICE=$(mapper get-service "$OBJECT") 406 INTERFACE=$STATE_INTERFACE.OperatingSystem.Status 407 PROPERTY=OperatingSystemState 408 state_query "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY 409 ;; 410 state|status) 411 for query in bmcstate chassisstate hoststate bootprogress osstate 412 do 413 handle_cmd $query 414 done 415 check_and_warn_boot_block 416 ;; 417 bootprogress) 418 OBJECT=$STATE_OBJECT/host$G_INSTANCE_ID 419 SERVICE=$(mapper get-service "$OBJECT") 420 INTERFACE=$STATE_INTERFACE.Boot.Progress 421 PROPERTY=BootProgress 422 state_query "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY 423 ;; 424 power) 425 OBJECT=/org/openbmc/control/power0 426 SERVICE=$(mapper get-service $OBJECT) 427 INTERFACE=org.openbmc.control.Power 428 for property in pgood state pgood_timeout; do 429 # get_property can potentially return several 430 # different formats of values, so we do the parsing outside 431 # of get_property depending on the query. These queries 432 # return 'i VALUE' formatted strings. 433 STATE=$(get_property "$SERVICE" "$OBJECT" "$INTERFACE" "$property" | sed 's/i[ ^I]*//') 434 printf "%s = %s\n" $property "$STATE" 435 done 436 ;; 437 chassiskill) 438 /usr/libexec/chassiskill 439 ;; 440 hostrebootoff) 441 OBJECT=$CONTROL_OBJECT/host$G_INSTANCE_ID/auto_reboot 442 SERVICE=$(mapper get-service "$OBJECT") 443 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 444 PROPERTY=AutoReboot 445 VALUE=false 446 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "b" $VALUE 447 ;; 448 hostrebootoffonetime) 449 OBJECT=$CONTROL_OBJECT/host$G_INSTANCE_ID/auto_reboot/one_time 450 SERVICE=$(mapper get-service "$OBJECT") 451 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 452 PROPERTY=AutoReboot 453 VALUE=false 454 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "b" $VALUE 455 ;; 456 hostrebooton) 457 OBJECT=$CONTROL_OBJECT/host$G_INSTANCE_ID/auto_reboot 458 SERVICE=$(mapper get-service "$OBJECT") 459 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 460 PROPERTY=AutoReboot 461 VALUE=true 462 set_property "$SERVICE" "$OBJECT" $INTERFACE $PROPERTY "b" $VALUE 463 ;; 464 bmcrebootoff) 465 disable_bmc_reboot 466 ;; 467 bmcrebooton) 468 enable_bmc_reboot 469 ;; 470 recoveryoff) 471 handle_cmd hostrebootoff 472 handle_cmd bmcrebootoff 473 mask_systemd_target $HOST_TIMEOUT_TARGET 474 mask_systemd_target $HOST_CRASH_TARGET 475 ;; 476 recoveryon) 477 handle_cmd hostrebooton 478 handle_cmd bmcrebooton 479 unmask_systemd_target $HOST_TIMEOUT_TARGET 480 unmask_systemd_target $HOST_CRASH_TARGET 481 ;; 482 recoverystatus) 483 host_reboot_state=$(get_host_reboot_status) 484 if [[ $host_reboot_state == "true" ]]; then 485 host_reboot_status=1 486 else 487 host_reboot_status=0 488 fi 489 490 bmc_reboot_state=$(get_bmc_reboot_status) 491 if [[ $bmc_reboot_state == "on" ]]; then 492 bmc_reboot_status=1 493 else 494 bmc_reboot_status=0 495 fi 496 497 host_timeout_target_state=$(get_systemd_target_state $HOST_TIMEOUT_TARGET) 498 if [[ $host_timeout_target_state == "masked" ]]; then 499 host_timeout_status=0 500 else 501 host_timeout_status=1 502 fi 503 504 host_crash_target_state=$(get_systemd_target_state $HOST_CRASH_TARGET) 505 if [[ $host_crash_target_state == "masked" ]]; then 506 host_crash_status=0 507 else 508 host_crash_status=1 509 fi 510 511 if (( host_reboot_status && bmc_reboot_status && host_timeout_status && host_crash_status )); then 512 echo "recovery: On" 513 elif (( !host_reboot_status && !bmc_reboot_status && !host_timeout_status && !host_crash_status )); then 514 echo "recovery: Off" 515 else 516 echo "recovery: Undefined" 517 fi 518 519 declare -A status 520 status[0]="Off" 521 status[1]="On" 522 523 printf " %-11s: %s\n" "hostReboot" "${status[$host_reboot_status]}" 524 printf " %-11s: %s\n" "bmcReboot" "${status[$bmc_reboot_status]}" 525 printf " %-11s: %s\n" "hostTimeout" "${status[$host_timeout_status]}" 526 printf " %-11s: %s\n" "hostCrash" "${status[$host_crash_status]}" 527 ;; 528 listbootblock) 529 blockingErrors=$(check_boot_block_errors) 530 if [ -z "$blockingErrors" ]; then 531 echo "No blocking errors present" 532 else 533 echo "$blockingErrors" 534 fi 535 ;; 536 listlogs) 537 list_logs 538 ;; 539 showlog) 540 show_log "$2" 541 ;; 542 deletelogs) 543 delete_logs 544 ;; 545 stopofftargets) 546 stop_off_targets 547 ;; 548 *) 549 print_usage_err "Invalid command '$1'" 550 ;; 551 esac 552} 553 554shiftcnt=0 555for arg in "$@"; do 556 case $arg in 557 -w|--wait) 558 G_WAIT=30 559 shiftcnt=$((shiftcnt+1)) 560 continue 561 ;; 562 -h|--help) 563 print_help 564 ;; 565 -v|--verbose) 566 G_VERBOSE=y 567 shiftcnt=$((shiftcnt+1)) 568 ;; 569 -i=*|--id=*) 570 G_INSTANCE_ID="${arg#*=}" 571 shiftcnt=$((shiftcnt+1)) 572 ;; 573 -*) 574 print_usage_err "Unknown option: $arg" 575 ;; 576 *) 577 G_ORIG_CMD=$arg 578 # shift out the optional parameters 579 shift $shiftcnt 580 # pass all arguments to handle_cmd in case command takes additional 581 # parameters 582 handle_cmd "$@" 583 break 584 ;; 585 esac 586done 587