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