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 deletelogs" 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 "obmcutil deletelogs Delete all phosphor-logging entries from" 73 echo " system" 74 echo "" 75 echo "optional arguments (must precede the positional options above):" 76 echo " -h, --help show this help message and exit" 77 echo " -w, --wait block until state transition succeeds or fails" 78 echo " -v, --verbose print the journal to stdout if --wait is supplied" 79 exit 0 80} 81 82run_timeout () 83{ 84 local timeout="$1"; shift 85 local cmd="$@" 86 local verbose_child= 87 88 if [ -n "$G_VERBOSE" ]; then 89 journalctl -f & 90 verbose_child=$! 91 fi 92 93 $cmd 94 95 # Run a background query for the transition to the expected state 96 # This will be killed if the transition doesn't succeed within 97 # a timeout period. 98 ( 99 while ! grep -q $G_REQUESTED_STATE <<< $(handle_cmd $G_QUERY) ; do 100 sleep 1 101 done 102 ) & 103 wait_child=$! 104 105 # Could be bad if process is killed before 'timeout' occurs if 106 # transition doesn't succeed. 107 trap -- "" SIGTERM 108 109 # Workaround for lack of 'timeout' command. 110 ( 111 sleep $timeout 112 kill $wait_child 113 ) > /dev/null 2>&1 & 114 115 if ! wait $wait_child; then 116 echo "Unable to confirm '$G_ORIG_CMD' success" \ 117 "within timeout period (${timeout}s)" 118 fi 119 120 if [ -n "$verbose_child" ]; then 121 kill $verbose_child 122 fi 123} 124 125run_cmd () 126{ 127 local cmd="$@"; 128 129 if [ -n "$G_WAIT" ]; then 130 run_timeout $G_WAIT "$cmd" 131 else 132 $cmd 133 fi 134} 135 136set_property () 137{ 138 run_cmd busctl set-property "$@" 139} 140 141get_property () 142{ 143 G_WAIT="" 144 run_cmd busctl get-property "$@" 145} 146 147state_query () 148{ 149 local state=$(get_property "$@" | cut -d '"' -f2) 150 printf "%-20s: %s\n" $4 $state 151} 152 153print_usage_err () 154{ 155 echo "ERROR: $1" >&2 156 echo "$USAGE" 157 exit 1 158} 159 160mask_systemd_target () 161{ 162 target="$@" 163 systemctl mask $target 164} 165 166unmask_systemd_target () 167{ 168 target="$@" 169 systemctl unmask $target 170} 171 172disable_bmc_reboot () 173{ 174 dir="/run/systemd/system/" 175 file="reboot-guard.conf" 176 units=("reboot" "poweroff" "halt") 177 178 for unit in "${units[@]}"; do 179 mkdir -p ${dir}${unit}.target.d 180 echo -e "[Unit]\nRefuseManualStart=yes" >> ${dir}${unit}.target.d/${file} 181 done 182} 183 184enable_bmc_reboot () 185{ 186 dir="/run/systemd/system/" 187 file="reboot-guard.conf" 188 units=("reboot" "poweroff" "halt") 189 190 for unit in "${units[@]}"; do 191 rm -rf ${dir}${unit}.target.d/${file} 192 rm -rf ${dir}${unit}.target.d 193 done 194} 195 196# will write blocking errors to stdout 197check_boot_block_errors () 198{ 199 # array of boot block objects 200 blockArray=() 201 202 # Look for any objects under logging that implement the 203 # xyz.openbmc_project.Logging.ErrorBlocksTransition 204 subtree="$(busctl call xyz.openbmc_project.ObjectMapper \ 205 /xyz/openbmc_project/object_mapper \ 206 xyz.openbmc_project.ObjectMapper \ 207 GetSubTree sias "/xyz/openbmc_project/logging/" 0 1 \ 208 xyz.openbmc_project.Logging.ErrorBlocksTransition)" 209 210 # remove quotation marks 211 subtree="$(echo $subtree | sed 's/\"//g')" 212 213 for entry in $subtree; do 214 if [[ ${entry} =~ "xyz/openbmc_project/logging/block"* ]]; then 215 blockArray+=( $entry ) 216 fi 217 done 218 219 # now find associated error log for each boot block error 220 for berror in "${blockArray[@]}"; do 221 assocs="$(busctl call xyz.openbmc_project.Logging $berror \ 222 org.freedesktop.DBus.Properties Get \ 223 ss xyz.openbmc_project.Association.Definitions Associations)" 224 225 # remove quotation marks 226 assocs="$(echo $assocs | sed 's/\"//g')" 227 228 for entry in $assocs; do 229 if [[ ${entry} =~ "xyz/openbmc_project/logging/entry"* ]]; then 230 echo "Blocking Error: $entry" 231 fi 232 done 233 done 234} 235 236# helper function to check for boot block errors and notify user 237check_and_warn_boot_block() 238{ 239 blockingErrors=$(check_boot_block_errors) 240 if ! [ -z "$blockingErrors" ]; then 241 echo !!!!!!!!!! 242 echo "WARNING! System has blocking errors that will prevent boot" 243 echo "$blockingErrors" 244 echo !!!!!!!!!! 245 fi 246} 247 248# list all phosphor-logging entries 249list_logs() 250{ 251 # Look for any objects under logging that implement the 252 # xyz.openbmc_project.Logging.Entry 253 busctl -j call xyz.openbmc_project.ObjectMapper \ 254 /xyz/openbmc_project/object_mapper \ 255 xyz.openbmc_project.ObjectMapper \ 256 GetSubTreePaths sias "/xyz/openbmc_project/logging/" 0 1 \ 257 xyz.openbmc_project.Logging.Entry 258} 259 260# display input log details 261show_log() 262{ 263 busctl -j call xyz.openbmc_project.Logging \ 264 $1 \ 265 org.freedesktop.DBus.Properties \ 266 GetAll s xyz.openbmc_project.Logging.Entry 267} 268 269# delete all phosphor-logging entries 270delete_logs() 271{ 272 busctl call xyz.openbmc_project.Logging \ 273 /xyz/openbmc_project/logging \ 274 xyz.openbmc_project.Collection.DeleteAll DeleteAll 275} 276 277handle_cmd () 278{ 279 case "$1" in 280 chassisoff) 281 OBJECT=$STATE_OBJECT/chassis0 282 SERVICE=$(mapper get-service $OBJECT) 283 INTERFACE=$STATE_INTERFACE.Chassis 284 PROPERTY=RequestedPowerTransition 285 VALUE=$INTERFACE.Transition.Off 286 G_REQUESTED_STATE=$INTERFACE.PowerState.Off 287 G_QUERY="chassisstate" 288 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 289 ;; 290 chassison) 291 check_and_warn_boot_block 292 OBJECT=$STATE_OBJECT/chassis0 293 SERVICE=$(mapper get-service $OBJECT) 294 INTERFACE=$STATE_INTERFACE.Chassis 295 PROPERTY=RequestedPowerTransition 296 VALUE=$INTERFACE.Transition.On 297 G_REQUESTED_STATE=$INTERFACE.PowerState.On 298 G_QUERY="chassisstate" 299 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 300 ;; 301 poweroff) 302 OBJECT=$STATE_OBJECT/host0 303 SERVICE=$(mapper get-service $OBJECT) 304 INTERFACE=$STATE_INTERFACE.Host 305 PROPERTY=RequestedHostTransition 306 VALUE=$INTERFACE.Transition.Off 307 G_REQUESTED_STATE=$INTERFACE.HostState.Off 308 G_QUERY="hoststate" 309 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 310 ;; 311 poweron) 312 check_and_warn_boot_block 313 OBJECT=$STATE_OBJECT/host0 314 SERVICE=$(mapper get-service $OBJECT) 315 INTERFACE=$STATE_INTERFACE.Host 316 PROPERTY=RequestedHostTransition 317 VALUE=$INTERFACE.Transition.On 318 G_REQUESTED_STATE=$INTERFACE.HostState.Running 319 G_QUERY="hoststate" 320 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "s" $VALUE 321 ;; 322 bmcstate) 323 OBJECT=$STATE_OBJECT/bmc0 324 SERVICE=$(mapper get-service $OBJECT) 325 INTERFACE=$STATE_INTERFACE.BMC 326 PROPERTY=CurrentBMCState 327 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 328 ;; 329 chassisstate) 330 OBJECT=$STATE_OBJECT/chassis0 331 SERVICE=$(mapper get-service $OBJECT) 332 INTERFACE=$STATE_INTERFACE.Chassis 333 PROPERTY=CurrentPowerState 334 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 335 ;; 336 hoststate) 337 OBJECT=$STATE_OBJECT/host0 338 SERVICE=$(mapper get-service $OBJECT) 339 INTERFACE=$STATE_INTERFACE.Host 340 PROPERTY=CurrentHostState 341 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 342 ;; 343 osstate) 344 OBJECT=$STATE_OBJECT/host0 345 SERVICE=$(mapper get-service $OBJECT) 346 INTERFACE=$STATE_INTERFACE.OperatingSystem.Status 347 PROPERTY=OperatingSystemState 348 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 349 ;; 350 state|status) 351 for query in bmcstate chassisstate hoststate bootprogress osstate 352 do 353 handle_cmd $query 354 done 355 check_and_warn_boot_block 356 ;; 357 bootprogress) 358 OBJECT=$STATE_OBJECT/host0 359 SERVICE=$(mapper get-service $OBJECT) 360 INTERFACE=$STATE_INTERFACE.Boot.Progress 361 PROPERTY=BootProgress 362 state_query $SERVICE $OBJECT $INTERFACE $PROPERTY 363 ;; 364 power) 365 OBJECT=/org/openbmc/control/power0 366 SERVICE=$(mapper get-service $OBJECT) 367 INTERFACE=org.openbmc.control.Power 368 for property in pgood state pgood_timeout 369 do 370 # get_property can potentially return several 371 # different formats of values, so we do the parsing outside 372 # of get_property depending on the query. These queries 373 # return 'i VALUE' formatted strings. 374 STATE=$(get_property $SERVICE $OBJECT $INTERFACE $property \ 375 | sed 's/i[ ^I]*//') 376 printf "%s = %s\n" $property $STATE 377 done 378 ;; 379 chassiskill) 380 /usr/libexec/chassiskill 381 ;; 382 hostrebootoff) 383 OBJECT=$CONTROL_OBJECT/host0/auto_reboot 384 SERVICE=$(mapper get-service $OBJECT) 385 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 386 PROPERTY=AutoReboot 387 VALUE=false 388 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "b" $VALUE 389 ;; 390 hostrebooton) 391 OBJECT=$CONTROL_OBJECT/host0/auto_reboot 392 SERVICE=$(mapper get-service $OBJECT) 393 INTERFACE=$CONTROL_INTERFACE.Boot.RebootPolicy 394 PROPERTY=AutoReboot 395 VALUE=true 396 set_property $SERVICE $OBJECT $INTERFACE $PROPERTY "b" $VALUE 397 ;; 398 bmcrebootoff) 399 disable_bmc_reboot 400 ;; 401 bmcrebooton) 402 enable_bmc_reboot 403 ;; 404 recoveryoff) 405 handle_cmd hostrebootoff 406 handle_cmd bmcrebootoff 407 mask_systemd_target $HOST_TIMEOUT_TARGET 408 mask_systemd_target $HOST_CRASH_TARGET 409 ;; 410 recoveryon) 411 handle_cmd hostrebooton 412 handle_cmd bmcrebooton 413 unmask_systemd_target $HOST_TIMEOUT_TARGET 414 unmask_systemd_target $HOST_CRASH_TARGET 415 ;; 416 listbootblock) 417 blockingErrors=$(check_boot_block_errors) 418 if [ -z "$blockingErrors" ]; then 419 echo "No blocking errors present" 420 else 421 echo "$blockingErrors" 422 fi 423 ;; 424 listlogs) 425 list_logs 426 ;; 427 showlog) 428 show_log $2 429 ;; 430 deletelogs) 431 delete_logs 432 ;; 433 *) 434 print_usage_err "Invalid command '$1'" 435 ;; 436 esac 437} 438 439shiftcnt=0 440for arg in "$@"; do 441 case $arg in 442 -w|--wait) 443 G_WAIT=30 444 shiftcnt=$((shiftcnt+1)) 445 continue 446 ;; 447 -h|--help) 448 print_help 449 ;; 450 -v|--verbose) 451 G_VERBOSE=y 452 shiftcnt=$((shiftcnt+1)) 453 ;; 454 -*) 455 print_usage_err "Unknown option: $arg" 456 ;; 457 *) 458 G_ORIG_CMD=$arg 459 # shift out the optional parameters 460 shift $shiftcnt 461 # pass all arguments to handle_cmd in case command takes additional 462 # parameters 463 handle_cmd "$@" 464 break 465 ;; 466 esac 467done 468