1#compdef busctl
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4# busctl(1) completion                               -*- shell-script -*-
5#
6# This file is part of systemd.
7#
8# systemd is free software; you can redistribute it and/or modify it
9# under the terms of the GNU Lesser General Public License as published by
10# the Free Software Foundation; either version 2.1 of the License, or
11# (at your option) any later version.
12#
13# systemd is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16# General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with systemd; If not, see <http://www.gnu.org/licenses/>.
20
21(( $+functions[_busctl_commands] )) || _busctl_commands()
22{
23    local -a _busctl_cmds
24    _busctl_cmds=(
25        "list:List bus names"
26        "status:Show bus service, process or bus owner credentials"
27        "monitor:Show bus traffic"
28        "capture:Capture bus traffic"
29        "tree:Show object tree of service"
30        "introspect:Introspect object"
31        "call:Call a method"
32        "get-property:Get property value"
33        "set-property:Set property value"
34    )
35    if (( CURRENT == 1 )); then
36        _describe -t commands 'busctl command' _busctl_cmds || compadd "$@"
37    else
38        local curcontext="$curcontext"
39        cmd="${${_busctl_cmds[(r)$words[1]:*]%%:*}}"
40        curcontext="${curcontext%:*:*}:busctl-${cmd}:"
41        if (( $+functions[_busctl_$cmd] )); then
42            _busctl_$cmd
43        else
44            _message "no more options"
45        fi
46    fi
47}
48
49__busctl() {
50    busctl $_bus_address --no-pager --no-legend "$@" 2>/dev/null
51}
52
53__dbus_matchspec() {
54    # https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing
55    _values -s, 'rules' \
56        'type[Match on message type]:type:(signal method_call method_return error)' \
57        'eavesdrop[Include unicast messages]:bool:(true false)' \
58        'sender[Match messages sent by a particular sender]:sender:{compadd $(_busctl_get_service_names)}'\
59        'interface[Match messages sent over or to a particular interface]:interface' \
60        'member[Match messages which have the given method or signal name]:member' \
61        'path[Match messages which are sent from or to the given object]:path' \
62        'path_namespace[Match messages which are sent from or to the given namespace]:namespace' \
63        'destination[Match messaged sent to the given unique name]:unique name:{compadd $(_busctl_get_unique_names)}'
64}
65
66(( $+functions[_busctl_get_json] )) || _busctl_get_json()
67{
68    local -a _json_forms
69    _json_forms=( $(__busctl --json=help; echo help) )
70    _values 'format' $_json_forms
71}
72
73(( $+functions[_busctl_get_service_names] )) || _busctl_get_service_names()
74{
75    local -a bus_names
76    bus_names=( $(__busctl call \
77        "org.freedesktop.DBus"  \
78        "/org/freedesktop/DBus" \
79        "org.freedesktop.DBus"  \
80        ListNames) )
81    echo ${(Q)bus_names[3,-1]}
82}
83
84(( $+functions[_busctl_get_unique_names] )) || _busctl_get_unique_names()
85{
86    local -a bus_names
87    local NAME OTHER
88    __busctl --unique list |
89    while read NAME OTHER; do
90        echo $NAME
91    done
92}
93
94(( $+functions[_busctl_get_objects] )) || _busctl_get_objects()
95{
96    local -a objects
97    local name="$1"
98    objects=($(__busctl --list tree $name ))
99    echo $objects
100}
101
102(( $+functions[_busctl_get_interfaces] )) || _busctl_get_interfaces()
103{
104    local NAME TYPE OTHER
105    __busctl introspect "$1" "$2" |
106    while read NAME TYPE OTHER; do
107        if [[ ${TYPE} == "interface" ]]; then
108            echo ${NAME}
109        fi
110    done
111}
112
113(( $+functions[_busctl_get_members] )) || _busctl_get_members()
114{
115    local member="$4"
116    local required="$5"
117    local NAME TYPE SIGNATURE VALUE FLAGS
118    __busctl introspect "$1" "$2" "$3" |
119    while read NAME TYPE SIGNATURE VALUE FLAGS; do
120        [[ -z "$member" || ${TYPE} == "$member" ]] &&
121        [[ -z "$required" || ${${(s: :)FLAGS}[-1]} == "$required" ]] &&
122        echo ${NAME#.}
123    done
124}
125
126(( $+functions[_busctl_get_signature] )) || _busctl_get_signature()
127{
128    local NAME TYPE SIGNATURE VALUE FLAGS
129    __busctl introspect "$1" "$2" "$3" |
130    while read NAME TYPE SIGNATURE VALUE FLAGS; do
131        if [[ ${NAME#.} == "$4" ]]; then
132            [[ ${SIGNATURE} != "-" ]] && echo ${SIGNATURE}
133        fi
134    done
135}
136
137(( $+functions[_busctl_status] )) || _busctl_status()
138{
139    local expl
140    _wanted busname expl 'busname' compadd "$@" - $(_busctl_get_service_names)
141}
142
143(( $+functions[_busctl_monitor] )) || _busctl_monitor()
144{
145    local expl
146    _wanted busname expl 'busname' compadd "$@" - $(_busctl_get_service_names)
147}
148
149(( $+functions[_busctl_tree] )) || _busctl_tree()
150{
151    local expl
152    _wanted busname expl 'busname' compadd "$@" - $(_busctl_get_service_names)
153}
154
155(( $+functions[_busctl_introspect] )) || _busctl_introspect()
156{
157    local expl
158    case $CURRENT in
159        2)
160            _wanted busname expl 'busname' \
161            compadd "$@" - $(_busctl_get_service_names)
162            ;;
163        3)
164            _wanted path expl 'path' \
165            compadd "$@" - $(_busctl_get_objects $words[2])
166            ;;
167        4)
168            _wanted interface expl 'interface' \
169            compadd "$@" - $(_busctl_get_interfaces $words[2,3])
170            ;;
171        *)
172            _message "no more options"
173    esac
174}
175
176(( $+functions[_busctl_call] )) || _busctl_call()
177{
178    local expl
179    case $CURRENT in
180        2)
181            _wanted busname expl 'busname' \
182            compadd "$@" - $(_busctl_get_service_names)
183            ;;
184        3)
185            _wanted path expl 'path' \
186            compadd "$@" - $(_busctl_get_objects $words[2])
187            ;;
188        4)
189            _wanted interface expl 'interface' \
190            compadd "$@" - $(_busctl_get_interfaces $words[2,3])
191            ;;
192        5)
193            _wanted method expl 'method' \
194            compadd "$@" - $(_busctl_get_members $words[2,4] "method")
195            ;;
196        6)
197            compadd "$@" - $(_busctl_get_signature $words[2,5])
198            ;;
199        *)
200            _message "no more options"
201    esac
202}
203
204(( $+functions[_busctl_get-property] )) || _busctl_get-property()
205{
206    local expl
207    case $CURRENT in
208        2)
209            _wanted busname expl 'busname' \
210            compadd "$@" - $(_busctl_get_service_names)
211            ;;
212        3)
213            _wanted path expl 'path' \
214            compadd "$@" - $(_busctl_get_objects $words[2])
215            ;;
216        4)
217            _wanted interface expl 'interface' \
218            compadd "$@" - $(_busctl_get_interfaces $words[2,3])
219            ;;
220        5)
221            _wanted property expl 'property' \
222            compadd "$@" - $(_busctl_get_members $words[2,4] "property")
223            ;;
224        *)
225            _message "no more options"
226    esac
227}
228
229(( $+functions[_busctl_set-property] )) || _busctl_set-property()
230{
231    local expl
232    case $CURRENT in
233        2)
234            _wanted busname expl 'busname' \
235            compadd "$@" - $(_busctl_get_service_names)
236            ;;
237        3)
238            _wanted path expl 'path' \
239            compadd "$@" - $(_busctl_get_objects $words[2])
240            ;;
241        4)
242            _wanted interface expl 'interface' \
243            compadd "$@" - $(_busctl_get_interfaces $words[2,3])
244            ;;
245        5)
246            _wanted property expl 'property' \
247            compadd "$@" - $(_busctl_get_members $words[2,4] "property" "writable")
248            ;;
249        6)
250            compadd "$@" - $(_busctl_get_signature $words[2,5])
251            ;;
252        *)
253            _message "no more options"
254    esac
255}
256
257local -a _modes; _modes=("--user" "--system")
258# Use the last mode (they are exclusive and the last one is used).
259local _bus_address=${${words:*_modes}[(R)(${(j.|.)_modes})]}
260local curcontext=$curcontext state line
261_arguments \
262    {-h,--help}'[Prints a short help text and exits.]' \
263    '--version[Prints a short version string and exits.]' \
264    '--no-pager[Do not pipe output into a pager]' \
265    '--no-legend[Do not show the headers and footers]' \
266    '--system[Connect to system manager]' \
267    '--user[Connect to user service manager]' \
268    {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
269    {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
270    '--address=[Connect to the bus specified by address]:address' \
271    '--show-machine[Show machine ID column in list]' \
272    '--unique[Only show unique names]' \
273    '--acquired[Only show acquired names]' \
274    '--activatable[Only show activatable names]' \
275    '--match=[Only show matching messages]:match:__dbus_matchspec' \
276    '--list[Do not show tree, but simple object path list]' \
277    {-q,--quiet}'[Do not show method call reply]'\
278    '--verbose[Show result values in long format]' \
279    '--json=[Show result values in long format]:format:_busctl_get_json' \
280    '-j[Show pretty json in interactive sessions, short json otherwise]' \
281    '--expect-reply=[Expect a method call reply]:boolean:(1 0)' \
282    '--auto-start=[Auto-start destination service]:boolean:(1 0)' \
283    '--allow-interactive-authorization=[Allow interactive authorization for operation]:boolean:(1 0)' \
284    '--timeout=[Maximum time to wait for method call completion]:timeout (seconds)' \
285    '--augment-creds=[Extend credential data with data read from /proc/$PID]:boolean:(1 0)' \
286    '*::busctl command:_busctl_commands'
287