1# bpftool(8) bash completion                               -*- shell-script -*-
2#
3# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
4# Copyright (C) 2017-2018 Netronome Systems, Inc.
5#
6# Author: Quentin Monnet <quentin.monnet@netronome.com>
7
8# Takes a list of words in argument; each one of them is added to COMPREPLY if
9# it is not already present on the command line. Returns no value.
10_bpftool_once_attr()
11{
12    local w idx found
13    for w in $*; do
14        found=0
15        for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
16            if [[ $w == ${words[idx]} ]]; then
17                found=1
18                break
19            fi
20        done
21        [[ $found -eq 0 ]] && \
22            COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
23    done
24}
25
26# Takes a list of words as argument; if any of those words is present on the
27# command line, return 0. Otherwise, return 1.
28_bpftool_search_list()
29{
30    local w idx
31    for w in $*; do
32        for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
33            [[ $w == ${words[idx]} ]] && return 0
34        done
35    done
36    return 1
37}
38
39# Takes a list of words in argument; adds them all to COMPREPLY if none of them
40# is already present on the command line. Returns no value.
41_bpftool_one_of_list()
42{
43    _bpftool_search_list $* && return 1
44    COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
45}
46
47_bpftool_get_map_ids()
48{
49    COMPREPLY+=( $( compgen -W "$( bpftool -jp map  2>&1 | \
50        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
51}
52
53# Takes map type and adds matching map ids to the list of suggestions.
54_bpftool_get_map_ids_for_type()
55{
56    local type="$1"
57    COMPREPLY+=( $( compgen -W "$( bpftool -jp map  2>&1 | \
58        command grep -C2 "$type" | \
59        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
60}
61
62_bpftool_get_map_names()
63{
64    COMPREPLY+=( $( compgen -W "$( bpftool -jp map  2>&1 | \
65        command sed -n 's/.*"name": \(.*\),$/\1/p' )" -- "$cur" ) )
66}
67
68# Takes map type and adds matching map names to the list of suggestions.
69_bpftool_get_map_names_for_type()
70{
71    local type="$1"
72    COMPREPLY+=( $( compgen -W "$( bpftool -jp map  2>&1 | \
73        command grep -C2 "$type" | \
74        command sed -n 's/.*"name": \(.*\),$/\1/p' )" -- "$cur" ) )
75}
76
77_bpftool_get_prog_ids()
78{
79    COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
80        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
81}
82
83_bpftool_get_prog_tags()
84{
85    COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
86        command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
87}
88
89_bpftool_get_prog_names()
90{
91    COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
92        command sed -n 's/.*"name": "\(.*\)",$/\1/p' )" -- "$cur" ) )
93}
94
95_bpftool_get_btf_ids()
96{
97    COMPREPLY+=( $( compgen -W "$( bpftool -jp btf 2>&1 | \
98        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
99}
100
101_bpftool_get_link_ids()
102{
103    COMPREPLY+=( $( compgen -W "$( bpftool -jp link 2>&1 | \
104        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
105}
106
107_bpftool_get_obj_map_names()
108{
109    local obj maps
110
111    obj=$1
112
113    maps=$(objdump -j .maps -t $obj 2>/dev/null | \
114        command awk '/g     . .maps/ {print $NF}')
115
116    COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) )
117}
118
119_bpftool_get_obj_map_idxs()
120{
121    local obj nmaps
122
123    obj=$1
124
125    nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g     . maps')
126
127    COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) )
128}
129
130_sysfs_get_netdevs()
131{
132    COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
133        "$cur" ) )
134}
135
136# Retrieve type of the map that we are operating on.
137_bpftool_map_guess_map_type()
138{
139    local keyword idx ref=""
140    for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
141        case "${words[$((idx-2))]}" in
142            lookup|update)
143                keyword=${words[$((idx-1))]}
144                ref=${words[$((idx))]}
145                ;;
146            push)
147                printf "stack"
148                return 0
149                ;;
150            enqueue)
151                printf "queue"
152                return 0
153                ;;
154        esac
155    done
156    [[ -z $ref ]] && return 0
157
158    local type
159    type=$(bpftool -jp map show $keyword $ref | \
160        command sed -n 's/.*"type": "\(.*\)",$/\1/p')
161    [[ -n $type ]] && printf $type
162}
163
164_bpftool_map_update_get_id()
165{
166    local command="$1"
167
168    # Is it the map to update, or a map to insert into the map to update?
169    # Search for "value" keyword.
170    local idx value
171    for (( idx=7; idx < ${#words[@]}-1; idx++ )); do
172        if [[ ${words[idx]} == "value" ]]; then
173            value=1
174            break
175        fi
176    done
177    if [[ $value -eq 0 ]]; then
178        case "$command" in
179            push)
180                _bpftool_get_map_ids_for_type stack
181                ;;
182            enqueue)
183                _bpftool_get_map_ids_for_type queue
184                ;;
185            *)
186                _bpftool_get_map_ids
187                ;;
188        esac
189        return 0
190    fi
191
192    # Id to complete is for a value. It can be either prog id or map id. This
193    # depends on the type of the map to update.
194    local type=$(_bpftool_map_guess_map_type)
195    case $type in
196        array_of_maps|hash_of_maps)
197            _bpftool_get_map_ids
198            return 0
199            ;;
200        prog_array)
201            _bpftool_get_prog_ids
202            return 0
203            ;;
204        *)
205            return 0
206            ;;
207    esac
208}
209
210_bpftool_map_update_get_name()
211{
212    local command="$1"
213
214    # Is it the map to update, or a map to insert into the map to update?
215    # Search for "value" keyword.
216    local idx value
217    for (( idx=7; idx < ${#words[@]}-1; idx++ )); do
218        if [[ ${words[idx]} == "value" ]]; then
219            value=1
220            break
221        fi
222    done
223    if [[ $value -eq 0 ]]; then
224        case "$command" in
225            push)
226                _bpftool_get_map_names_for_type stack
227                ;;
228            enqueue)
229                _bpftool_get_map_names_for_type queue
230                ;;
231            *)
232                _bpftool_get_map_names
233                ;;
234        esac
235        return 0
236    fi
237
238    # Name to complete is for a value. It can be either prog name or map name. This
239    # depends on the type of the map to update.
240    local type=$(_bpftool_map_guess_map_type)
241    case $type in
242        array_of_maps|hash_of_maps)
243            _bpftool_get_map_names
244            return 0
245            ;;
246        prog_array)
247            _bpftool_get_prog_names
248            return 0
249            ;;
250        *)
251            return 0
252            ;;
253    esac
254}
255
256_bpftool()
257{
258    local cur prev words cword comp_args
259    local json=0
260    _init_completion -- "$@" || return
261
262    # Deal with options
263    if [[ ${words[cword]} == -* ]]; then
264        local c='--version --json --pretty --bpffs --mapcompat --debug \
265            --use-loader --base-btf'
266        COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
267        return 0
268    fi
269    if _bpftool_search_list -j --json -p --pretty; then
270        json=1
271    fi
272
273    # Deal with simplest keywords
274    case $prev in
275        help|hex)
276            return 0
277            ;;
278        tag)
279            _bpftool_get_prog_tags
280            return 0
281            ;;
282        dev|offload_dev|xdpmeta_dev)
283            _sysfs_get_netdevs
284            return 0
285            ;;
286        file|pinned|-B|--base-btf)
287            _filedir
288            return 0
289            ;;
290        batch)
291            COMPREPLY=( $( compgen -W 'file' -- "$cur" ) )
292            return 0
293            ;;
294    esac
295
296    # Remove all options so completions don't have to deal with them.
297    local i pprev
298    for (( i=1; i < ${#words[@]}; )); do
299        if [[ ${words[i]::1} == - ]] &&
300            [[ ${words[i]} != "-B" ]] && [[ ${words[i]} != "--base-btf" ]]; then
301            words=( "${words[@]:0:i}" "${words[@]:i+1}" )
302            [[ $i -le $cword ]] && cword=$(( cword - 1 ))
303        else
304            i=$(( ++i ))
305        fi
306    done
307    cur=${words[cword]}
308    prev=${words[cword - 1]}
309    pprev=${words[cword - 2]}
310
311    local object=${words[1]}
312
313    if [[ -z $object || $cword -eq 1 ]]; then
314        case $cur in
315            *)
316                COMPREPLY=( $( compgen -W "$( bpftool help 2>&1 | \
317                    command sed \
318                    -e '/OBJECT := /!d' \
319                    -e 's/.*{//' \
320                    -e 's/}.*//' \
321                    -e 's/|//g' )" -- "$cur" ) )
322                COMPREPLY+=( $( compgen -W 'batch help' -- "$cur" ) )
323                return 0
324                ;;
325        esac
326    fi
327
328    local command=${words[2]}
329    [[ $command == help ]] && return 0
330
331    local MAP_TYPE='id pinned name'
332    local PROG_TYPE='id pinned tag name'
333
334    # Completion depends on object and command in use
335    case $object in
336        prog)
337            # Complete id and name, only for subcommands that use prog (but no
338            # map) ids/names.
339            case $command in
340                show|list|dump|pin)
341                    case $prev in
342                        id)
343                            _bpftool_get_prog_ids
344                            return 0
345                            ;;
346                        name)
347                            _bpftool_get_prog_names
348                            return 0
349                            ;;
350                    esac
351                    ;;
352            esac
353
354            local METRIC_TYPE='cycles instructions l1d_loads llc_misses \
355                itlb_misses dtlb_misses'
356            case $command in
357                show|list)
358                    [[ $prev != "$command" ]] && return 0
359                    COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
360                    return 0
361                    ;;
362                dump)
363                    case $prev in
364                        $command)
365                            COMPREPLY+=( $( compgen -W "xlated jited" -- \
366                                "$cur" ) )
367                            return 0
368                            ;;
369                        xlated|jited)
370                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
371                                "$cur" ) )
372                            return 0
373                            ;;
374                        *)
375                            # "file" is not compatible with other keywords here
376                            if _bpftool_search_list 'file'; then
377                                return 0
378                            fi
379                            if ! _bpftool_search_list 'linum opcodes visual'; then
380                                _bpftool_once_attr 'file'
381                            fi
382                            _bpftool_once_attr 'linum opcodes'
383                            if _bpftool_search_list 'xlated' && [[ "$json" == 0 ]]; then
384                                _bpftool_once_attr 'visual'
385                            fi
386                            return 0
387                            ;;
388                    esac
389                    ;;
390                pin)
391                    if [[ $prev == "$command" ]]; then
392                        COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
393                    else
394                        _filedir
395                    fi
396                    return 0
397                    ;;
398                attach|detach)
399                    case $cword in
400                        3)
401                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
402                            return 0
403                            ;;
404                        4)
405                            case $prev in
406                                id)
407                                    _bpftool_get_prog_ids
408                                    ;;
409                                name)
410                                    _bpftool_get_prog_names
411                                    ;;
412                                pinned)
413                                    _filedir
414                                    ;;
415                            esac
416                            return 0
417                            ;;
418                        5)
419                            local BPFTOOL_PROG_ATTACH_TYPES='sk_msg_verdict \
420                                sk_skb_verdict sk_skb_stream_verdict sk_skb_stream_parser \
421                                flow_dissector'
422                            COMPREPLY=( $( compgen -W "$BPFTOOL_PROG_ATTACH_TYPES" -- "$cur" ) )
423                            return 0
424                            ;;
425                        6)
426                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
427                            return 0
428                            ;;
429                        7)
430                            case $prev in
431                                id)
432                                    _bpftool_get_map_ids
433                                    ;;
434                                name)
435                                    _bpftool_get_map_names
436                                    ;;
437                                pinned)
438                                    _filedir
439                                    ;;
440                            esac
441                            return 0
442                            ;;
443                    esac
444                    ;;
445                load|loadall)
446                    local obj
447
448                    # Propose "load/loadall" to complete "bpftool prog load",
449                    # or bash tries to complete "load" as a filename below.
450                    if [[ ${#words[@]} -eq 3 ]]; then
451                        COMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) )
452                        return 0
453                    fi
454
455                    if [[ ${#words[@]} -lt 6 ]]; then
456                        _filedir
457                        return 0
458                    fi
459
460                    obj=${words[3]}
461
462                    if [[ ${words[-4]} == "map" ]]; then
463                        COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
464                        return 0
465                    fi
466                    if [[ ${words[-3]} == "map" ]]; then
467                        if [[ ${words[-2]} == "idx" ]]; then
468                            _bpftool_get_obj_map_idxs $obj
469                        elif [[ ${words[-2]} == "name" ]]; then
470                            _bpftool_get_obj_map_names $obj
471                        fi
472                        return 0
473                    fi
474                    if [[ ${words[-2]} == "map" ]]; then
475                        COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )
476                        return 0
477                    fi
478
479                    case $prev in
480                        type)
481                            local BPFTOOL_PROG_LOAD_TYPES='socket kprobe \
482                                kretprobe classifier flow_dissector \
483                                action tracepoint raw_tracepoint \
484                                xdp perf_event cgroup/skb cgroup/sock \
485                                cgroup/dev lwt_in lwt_out lwt_xmit \
486                                lwt_seg6local sockops sk_skb sk_msg lirc_mode2 \
487                                cgroup/bind4 cgroup/bind6 \
488                                cgroup/connect4 cgroup/connect6 cgroup/connect_unix \
489                                cgroup/getpeername4 cgroup/getpeername6 cgroup/getpeername_unix \
490                                cgroup/getsockname4 cgroup/getsockname6 cgroup/getsockname_unix \
491                                cgroup/sendmsg4 cgroup/sendmsg6 cgroup/sendmsg_unix \
492                                cgroup/recvmsg4 cgroup/recvmsg6 cgroup/recvmsg_unix \
493                                cgroup/post_bind4 cgroup/post_bind6 \
494                                cgroup/sysctl cgroup/getsockopt \
495                                cgroup/setsockopt cgroup/sock_release struct_ops \
496                                fentry fexit freplace sk_lookup'
497                            COMPREPLY=( $( compgen -W "$BPFTOOL_PROG_LOAD_TYPES" -- "$cur" ) )
498                            return 0
499                            ;;
500                        id)
501                            _bpftool_get_map_ids
502                            return 0
503                            ;;
504                        name)
505                            _bpftool_get_map_names
506                            return 0
507                            ;;
508                        pinned|pinmaps|kernel_btf)
509                            _filedir
510                            return 0
511                            ;;
512                        *)
513                            COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
514                            _bpftool_once_attr 'type pinmaps autoattach kernel_btf'
515                            _bpftool_one_of_list 'offload_dev xdpmeta_dev'
516                            return 0
517                            ;;
518                    esac
519                    ;;
520                tracelog)
521                    case $prev in
522                        $command)
523                            COMPREPLY+=( $( compgen -W "stdout stderr" -- \
524                                "$cur" ) )
525                            return 0
526                            ;;
527                        stdout|stderr)
528                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
529                                "$cur" ) )
530                            return 0
531                            ;;
532                        *)
533                            return 0
534                            ;;
535                    esac
536                    ;;
537                profile)
538                    case $cword in
539                        3)
540                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
541                            return 0
542                            ;;
543                        4)
544                            case $prev in
545                                id)
546                                    _bpftool_get_prog_ids
547                                    ;;
548                                name)
549                                    _bpftool_get_prog_names
550                                    ;;
551                                pinned)
552                                    _filedir
553                                    ;;
554                            esac
555                            return 0
556                            ;;
557                        5)
558                            COMPREPLY=( $( compgen -W "$METRIC_TYPE duration" -- "$cur" ) )
559                            return 0
560                            ;;
561                        *)
562                            [[ $prev == duration ]] && return 0
563                            _bpftool_once_attr "$METRIC_TYPE"
564                            return 0
565                            ;;
566                    esac
567                    ;;
568                run)
569                    if [[ ${#words[@]} -eq 4 ]]; then
570                        COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
571                        return 0
572                    fi
573                    case $prev in
574                        id)
575                            _bpftool_get_prog_ids
576                            return 0
577                            ;;
578                        name)
579                            _bpftool_get_prog_names
580                            return 0
581                            ;;
582                        data_in|data_out|ctx_in|ctx_out)
583                            _filedir
584                            return 0
585                            ;;
586                        repeat|data_size_out|ctx_size_out)
587                            return 0
588                            ;;
589                        *)
590                            _bpftool_once_attr 'data_in data_out data_size_out \
591                                ctx_in ctx_out ctx_size_out repeat'
592                            return 0
593                            ;;
594                    esac
595                    ;;
596                *)
597                    [[ $prev == $object ]] && \
598                        COMPREPLY=( $( compgen -W 'dump help pin attach detach \
599                            load loadall show list tracelog run profile' -- "$cur" ) )
600                    ;;
601            esac
602            ;;
603        struct_ops)
604            local STRUCT_OPS_TYPE='id name'
605            case $command in
606                show|list|dump|unregister)
607                    case $prev in
608                        $command)
609                            COMPREPLY=( $( compgen -W "$STRUCT_OPS_TYPE" -- "$cur" ) )
610                            ;;
611                        id)
612                            _bpftool_get_map_ids_for_type struct_ops
613                            ;;
614                        name)
615                            _bpftool_get_map_names_for_type struct_ops
616                            ;;
617                    esac
618                    return 0
619                    ;;
620                register)
621                    [[ $prev == $command ]] && _filedir
622                    return 0
623                    ;;
624                *)
625                    [[ $prev == $object ]] && \
626                        COMPREPLY=( $( compgen -W 'register unregister show list dump help' \
627                            -- "$cur" ) )
628                    ;;
629            esac
630            ;;
631        iter)
632            case $command in
633                pin)
634                    case $prev in
635                        $command)
636                            _filedir
637                            ;;
638                        id)
639                            _bpftool_get_map_ids
640                            ;;
641                        name)
642                            _bpftool_get_map_names
643                            ;;
644                        pinned)
645                            _filedir
646                            ;;
647                        map)
648                            _bpftool_one_of_list $MAP_TYPE
649                            ;;
650                        *)
651                            _bpftool_once_attr 'map'
652                            ;;
653                    esac
654                    return 0
655                    ;;
656                *)
657                    [[ $prev == $object ]] && \
658                        COMPREPLY=( $( compgen -W 'pin help' \
659                            -- "$cur" ) )
660                    ;;
661            esac
662            ;;
663        map)
664            case $command in
665                show|list|dump|peek|pop|dequeue|freeze)
666                    case $prev in
667                        $command)
668                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
669                            return 0
670                            ;;
671                        id)
672                            case "$command" in
673                                peek)
674                                    _bpftool_get_map_ids_for_type stack
675                                    _bpftool_get_map_ids_for_type queue
676                                    ;;
677                                pop)
678                                    _bpftool_get_map_ids_for_type stack
679                                    ;;
680                                dequeue)
681                                    _bpftool_get_map_ids_for_type queue
682                                    ;;
683                                *)
684                                    _bpftool_get_map_ids
685                                    ;;
686                            esac
687                            return 0
688                            ;;
689                        name)
690                            case "$command" in
691                                peek)
692                                    _bpftool_get_map_names_for_type stack
693                                    _bpftool_get_map_names_for_type queue
694                                    ;;
695                                pop)
696                                    _bpftool_get_map_names_for_type stack
697                                    ;;
698                                dequeue)
699                                    _bpftool_get_map_names_for_type queue
700                                    ;;
701                                *)
702                                    _bpftool_get_map_names
703                                    ;;
704                            esac
705                            return 0
706                            ;;
707                        *)
708                            return 0
709                            ;;
710                    esac
711                    ;;
712                create)
713                    case $prev in
714                        $command)
715                            _filedir
716                            return 0
717                            ;;
718                        type)
719                            local BPFTOOL_MAP_CREATE_TYPES="$(bpftool feature list_builtins map_types 2>/dev/null | \
720                                grep -v '^unspec$')"
721                            COMPREPLY=( $( compgen -W "$BPFTOOL_MAP_CREATE_TYPES" -- "$cur" ) )
722                            return 0
723                            ;;
724                        key|value|flags|entries)
725                            return 0
726                            ;;
727                        inner_map)
728                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
729                            return 0
730                            ;;
731                        id)
732                            _bpftool_get_map_ids
733                            ;;
734                        name)
735                            case $pprev in
736                                inner_map)
737                                    _bpftool_get_map_names
738                                    ;;
739                                *)
740                                    return 0
741                                    ;;
742                            esac
743                            ;;
744                        *)
745                            _bpftool_once_attr 'type key value entries name flags offload_dev'
746                            if _bpftool_search_list 'array_of_maps' 'hash_of_maps'; then
747                                _bpftool_once_attr 'inner_map'
748                            fi
749                            return 0
750                            ;;
751                    esac
752                    ;;
753                lookup|getnext|delete)
754                    case $prev in
755                        $command)
756                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
757                            return 0
758                            ;;
759                        id)
760                            _bpftool_get_map_ids
761                            return 0
762                            ;;
763                        name)
764                            _bpftool_get_map_names
765                            return 0
766                            ;;
767                        key)
768                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
769                            ;;
770                        *)
771                            case $(_bpftool_map_guess_map_type) in
772                                queue|stack)
773                                    return 0
774                                    ;;
775                            esac
776
777                            _bpftool_once_attr 'key'
778                            return 0
779                            ;;
780                    esac
781                    ;;
782                update|push|enqueue)
783                    case $prev in
784                        $command)
785                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
786                            return 0
787                            ;;
788                        id)
789                            _bpftool_map_update_get_id $command
790                            return 0
791                            ;;
792                        name)
793                            _bpftool_map_update_get_name $command
794                            return 0
795                            ;;
796                        key)
797                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
798                            ;;
799                        value)
800                            # We can have bytes, or references to a prog or a
801                            # map, depending on the type of the map to update.
802                            case "$(_bpftool_map_guess_map_type)" in
803                                array_of_maps|hash_of_maps)
804                                    COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
805                                        -- "$cur" ) )
806                                    return 0
807                                    ;;
808                                prog_array)
809                                    COMPREPLY+=( $( compgen -W "$PROG_TYPE" \
810                                        -- "$cur" ) )
811                                    return 0
812                                    ;;
813                                *)
814                                    COMPREPLY+=( $( compgen -W 'hex' \
815                                        -- "$cur" ) )
816                                    return 0
817                                    ;;
818                            esac
819                            return 0
820                            ;;
821                        *)
822                            case $(_bpftool_map_guess_map_type) in
823                                queue|stack)
824                                    _bpftool_once_attr 'value'
825                                    return 0;
826                                    ;;
827                            esac
828
829                            _bpftool_once_attr 'key'
830                            local UPDATE_FLAGS='any exist noexist' idx
831                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
832                                if [[ ${words[idx]} == 'value' ]]; then
833                                    # 'value' is present, but is not the last
834                                    # word i.e. we can now have UPDATE_FLAGS.
835                                    _bpftool_one_of_list "$UPDATE_FLAGS"
836                                    return 0
837                                fi
838                            done
839                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
840                                if [[ ${words[idx]} == 'key' ]]; then
841                                    # 'key' is present, but is not the last
842                                    # word i.e. we can now have 'value'.
843                                    _bpftool_once_attr 'value'
844                                    return 0
845                                fi
846                            done
847
848                            return 0
849                            ;;
850                    esac
851                    ;;
852                pin)
853                    case $prev in
854                        $command)
855                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
856                            ;;
857                        id)
858                            _bpftool_get_map_ids
859                            ;;
860                        name)
861                            _bpftool_get_map_names
862                            ;;
863                    esac
864                    return 0
865                    ;;
866                event_pipe)
867                    case $prev in
868                        $command)
869                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
870                            return 0
871                            ;;
872                        id)
873                            _bpftool_get_map_ids_for_type perf_event_array
874                            return 0
875                            ;;
876                        name)
877                            _bpftool_get_map_names_for_type perf_event_array
878                            return 0
879                            ;;
880                        cpu)
881                            return 0
882                            ;;
883                        index)
884                            return 0
885                            ;;
886                        *)
887                            _bpftool_once_attr 'cpu index'
888                            return 0
889                            ;;
890                    esac
891                    ;;
892                *)
893                    [[ $prev == $object ]] && \
894                        COMPREPLY=( $( compgen -W 'delete dump getnext help \
895                            lookup pin event_pipe show list update create \
896                            peek push enqueue pop dequeue freeze' -- \
897                            "$cur" ) )
898                    ;;
899            esac
900            ;;
901        btf)
902            local MAP_TYPE='id pinned name'
903            case $command in
904                dump)
905                    case $prev in
906                        $command)
907                            COMPREPLY+=( $( compgen -W "id map prog file" -- \
908                                "$cur" ) )
909                            return 0
910                            ;;
911                        prog)
912                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
913                            return 0
914                            ;;
915                        map)
916                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
917                            return 0
918                            ;;
919                        id)
920                            case $pprev in
921                                prog)
922                                    _bpftool_get_prog_ids
923                                    ;;
924                                map)
925                                    _bpftool_get_map_ids
926                                    ;;
927                                $command)
928                                    _bpftool_get_btf_ids
929                                    ;;
930                            esac
931                            return 0
932                            ;;
933                        name)
934                            case $pprev in
935                                prog)
936                                    _bpftool_get_prog_names
937                                    ;;
938                                map)
939                                    _bpftool_get_map_names
940                                    ;;
941                            esac
942                            return 0
943                            ;;
944                        format)
945                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
946                            ;;
947                        root_id)
948                            return 0;
949                            ;;
950                        c)
951                            COMPREPLY=( $( compgen -W "unsorted root_id" -- "$cur" ) )
952                            ;;
953                        *)
954                            # emit extra options
955                            case ${words[3]} in
956                                id|file)
957                                    COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
958                                    _bpftool_once_attr 'format'
959                                    ;;
960                                map|prog)
961                                    if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
962                                        COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
963                                    fi
964                                    COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
965                                    _bpftool_once_attr 'format'
966                                    ;;
967                                *)
968                                    ;;
969                            esac
970                            return 0
971                            ;;
972                    esac
973                    ;;
974                show|list)
975                    case $prev in
976                        $command)
977                            COMPREPLY+=( $( compgen -W "id" -- "$cur" ) )
978                            ;;
979                        id)
980                            _bpftool_get_btf_ids
981                            ;;
982                    esac
983                    return 0
984                    ;;
985                *)
986                    [[ $prev == $object ]] && \
987                        COMPREPLY=( $( compgen -W 'dump help show list' \
988                            -- "$cur" ) )
989                    ;;
990            esac
991            ;;
992        gen)
993            case $command in
994                object)
995                    _filedir
996                    return 0
997                    ;;
998                skeleton)
999                    case $prev in
1000                        $command)
1001                            _filedir
1002                            return 0
1003                            ;;
1004                        *)
1005                            _bpftool_once_attr 'name'
1006                            return 0
1007                            ;;
1008                    esac
1009                    ;;
1010                subskeleton)
1011                    case $prev in
1012                        $command)
1013                            _filedir
1014                            return 0
1015                            ;;
1016                        *)
1017                            _bpftool_once_attr 'name'
1018                            return 0
1019                            ;;
1020                    esac
1021                    ;;
1022                min_core_btf)
1023                    _filedir
1024                    return 0
1025                    ;;
1026                *)
1027                    [[ $prev == $object ]] && \
1028                        COMPREPLY=( $( compgen -W 'object skeleton subskeleton help min_core_btf' -- "$cur" ) )
1029                    ;;
1030            esac
1031            ;;
1032        cgroup)
1033            case $command in
1034                show|list|tree)
1035                    case $cword in
1036                        3)
1037                            _filedir
1038                            ;;
1039                        4)
1040                            COMPREPLY=( $( compgen -W 'effective' -- "$cur" ) )
1041                            ;;
1042                    esac
1043                    return 0
1044                    ;;
1045                attach|detach)
1046                    local BPFTOOL_CGROUP_ATTACH_TYPES="$(bpftool feature list_builtins attach_types 2>/dev/null | \
1047                        grep '^cgroup_')"
1048                    local ATTACH_FLAGS='multi override'
1049                    # Check for $prev = $command first
1050                    if [ $prev = $command ]; then
1051                        _filedir
1052                        return 0
1053                    # Then check for attach type. This is done outside of the
1054                    # "case $prev in" to avoid writing the whole list of attach
1055                    # types again as pattern to match (where we cannot reuse
1056                    # our variable).
1057                    elif [[ $BPFTOOL_CGROUP_ATTACH_TYPES =~ $prev ]]; then
1058                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
1059                                "$cur" ) )
1060                            return 0
1061                    fi
1062                    # case/esac for the other cases
1063                    case $prev in
1064                        id)
1065                            _bpftool_get_prog_ids
1066                            return 0
1067                            ;;
1068                        *)
1069                            if ! _bpftool_search_list "$BPFTOOL_CGROUP_ATTACH_TYPES"; then
1070                                COMPREPLY=( $( compgen -W \
1071                                    "$BPFTOOL_CGROUP_ATTACH_TYPES" -- "$cur" ) )
1072                            elif [[ "$command" == "attach" ]]; then
1073                                # We have an attach type on the command line,
1074                                # but it is not the previous word, or
1075                                # "id|pinned|tag|name" (we already checked for
1076                                # that). This should only leave the case when
1077                                # we need attach flags for "attach" commamnd.
1078                                _bpftool_one_of_list "$ATTACH_FLAGS"
1079                            fi
1080                            return 0
1081                            ;;
1082                    esac
1083                    ;;
1084                *)
1085                    [[ $prev == $object ]] && \
1086                        COMPREPLY=( $( compgen -W 'help attach detach \
1087                            show list tree' -- "$cur" ) )
1088                    ;;
1089            esac
1090            ;;
1091        perf)
1092            case $command in
1093                *)
1094                    [[ $prev == $object ]] && \
1095                        COMPREPLY=( $( compgen -W 'help \
1096                            show list' -- "$cur" ) )
1097                    ;;
1098            esac
1099            ;;
1100        net)
1101            local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload tcx_ingress tcx_egress'
1102            case $command in
1103                show|list)
1104                    [[ $prev != "$command" ]] && return 0
1105                    COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1106                    return 0
1107                    ;;
1108                attach)
1109                    case $cword in
1110                        3)
1111                            COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
1112                            return 0
1113                            ;;
1114                        4)
1115                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
1116                            return 0
1117                            ;;
1118                        5)
1119                            case $prev in
1120                                id)
1121                                    _bpftool_get_prog_ids
1122                                    ;;
1123                                name)
1124                                    _bpftool_get_prog_names
1125                                    ;;
1126                                pinned)
1127                                    _filedir
1128                                    ;;
1129                            esac
1130                            return 0
1131                            ;;
1132                        6)
1133                            COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1134                            return 0
1135                            ;;
1136                        8)
1137                            _bpftool_once_attr 'overwrite'
1138                            return 0
1139                            ;;
1140                    esac
1141                    ;;
1142                detach)
1143                    case $cword in
1144                        3)
1145                            COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
1146                            return 0
1147                            ;;
1148                        4)
1149                            COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1150                            return 0
1151                            ;;
1152                    esac
1153                    ;;
1154                *)
1155                    [[ $prev == $object ]] && \
1156                        COMPREPLY=( $( compgen -W 'help \
1157                            show list attach detach' -- "$cur" ) )
1158                    ;;
1159            esac
1160            ;;
1161        feature)
1162            case $command in
1163                probe)
1164                    [[ $prev == "prefix" ]] && return 0
1165                    if _bpftool_search_list 'macros'; then
1166                        _bpftool_once_attr 'prefix'
1167                    else
1168                        COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )
1169                    fi
1170                    _bpftool_one_of_list 'kernel dev'
1171                    _bpftool_once_attr 'full unprivileged'
1172                    return 0
1173                    ;;
1174                list_builtins)
1175                    [[ $prev != "$command" ]] && return 0
1176                    COMPREPLY=( $( compgen -W 'prog_types map_types \
1177                        attach_types link_types helpers' -- "$cur" ) )
1178                    ;;
1179                *)
1180                    [[ $prev == $object ]] && \
1181                        COMPREPLY=( $( compgen -W 'help list_builtins probe' -- "$cur" ) )
1182                    ;;
1183            esac
1184            ;;
1185        link)
1186            case $command in
1187                show|list|pin|detach)
1188                    case $prev in
1189                        id)
1190                            _bpftool_get_link_ids
1191                            return 0
1192                            ;;
1193                    esac
1194                    ;;
1195            esac
1196
1197            local LINK_TYPE='id pinned'
1198            case $command in
1199                show|list)
1200                    [[ $prev != "$command" ]] && return 0
1201                    COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
1202                    return 0
1203                    ;;
1204                pin|detach)
1205                    if [[ $prev == "$command" ]]; then
1206                        COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
1207                    elif [[ $pprev == "$command" ]]; then
1208                        _filedir
1209                    fi
1210                    return 0
1211                    ;;
1212                *)
1213                    [[ $prev == $object ]] && \
1214                        COMPREPLY=( $( compgen -W 'help pin detach show list' -- "$cur" ) )
1215                    ;;
1216            esac
1217            ;;
1218    esac
1219} &&
1220complete -F _bpftool bpftool
1221
1222# ex: ts=4 sw=4 et filetype=sh
1223