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
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
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 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 objword json=0
259    _init_completion || return
260
261    # Deal with options
262    if [[ ${words[cword]} == -* ]]; then
263        local c='--version --json --pretty --bpffs --mapcompat --debug \
264            --use-loader --base-btf'
265        COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
266        return 0
267    fi
268    if _bpftool_search_list -j --json -p --pretty; then
269        json=1
270    fi
271
272    # Deal with simplest keywords
273    case $prev in
274        help|hex)
275            return 0
276            ;;
277        tag)
278            _bpftool_get_prog_tags
279            return 0
280            ;;
281        dev|offload_dev|xdpmeta_dev)
282            _sysfs_get_netdevs
283            return 0
284            ;;
285        file|pinned|-B|--base-btf)
286            _filedir
287            return 0
288            ;;
289        batch)
290            COMPREPLY=( $( compgen -W 'file' -- "$cur" ) )
291            return 0
292            ;;
293    esac
294
295    # Remove all options so completions don't have to deal with them.
296    local i
297    for (( i=1; i < ${#words[@]}; )); do
298        if [[ ${words[i]::1} == - ]] &&
299            [[ ${words[i]} != "-B" ]] && [[ ${words[i]} != "--base-btf" ]]; then
300            words=( "${words[@]:0:i}" "${words[@]:i+1}" )
301            [[ $i -le $cword ]] && cword=$(( cword - 1 ))
302        else
303            i=$(( ++i ))
304        fi
305    done
306    cur=${words[cword]}
307    prev=${words[cword - 1]}
308    pprev=${words[cword - 2]}
309
310    local object=${words[1]} command=${words[2]}
311
312    if [[ -z $object || $cword -eq 1 ]]; then
313        case $cur in
314            *)
315                COMPREPLY=( $( compgen -W "$( bpftool help 2>&1 | \
316                    command sed \
317                    -e '/OBJECT := /!d' \
318                    -e 's/.*{//' \
319                    -e 's/}.*//' \
320                    -e 's/|//g' )" -- "$cur" ) )
321                COMPREPLY+=( $( compgen -W 'batch help' -- "$cur" ) )
322                return 0
323                ;;
324        esac
325    fi
326
327    [[ $command == help ]] && return 0
328
329    # Completion depends on object and command in use
330    case $object in
331        prog)
332            # Complete id and name, only for subcommands that use prog (but no
333            # map) ids/names.
334            case $command in
335                show|list|dump|pin)
336                    case $prev in
337                        id)
338                            _bpftool_get_prog_ids
339                            return 0
340                            ;;
341                        name)
342                            _bpftool_get_prog_names
343                            return 0
344                            ;;
345                    esac
346                    ;;
347            esac
348
349            local PROG_TYPE='id pinned tag name'
350            local MAP_TYPE='id pinned name'
351            local METRIC_TYPE='cycles instructions l1d_loads llc_misses \
352                itlb_misses dtlb_misses'
353            case $command in
354                show|list)
355                    [[ $prev != "$command" ]] && return 0
356                    COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
357                    return 0
358                    ;;
359                dump)
360                    case $prev in
361                        $command)
362                            COMPREPLY+=( $( compgen -W "xlated jited" -- \
363                                "$cur" ) )
364                            return 0
365                            ;;
366                        xlated|jited)
367                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
368                                "$cur" ) )
369                            return 0
370                            ;;
371                        *)
372                            # "file" is not compatible with other keywords here
373                            if _bpftool_search_list 'file'; then
374                                return 0
375                            fi
376                            if ! _bpftool_search_list 'linum opcodes visual'; then
377                                _bpftool_once_attr 'file'
378                            fi
379                            _bpftool_once_attr 'linum opcodes'
380                            if _bpftool_search_list 'xlated' && [[ "$json" == 0 ]]; then
381                                _bpftool_once_attr 'visual'
382                            fi
383                            return 0
384                            ;;
385                    esac
386                    ;;
387                pin)
388                    if [[ $prev == "$command" ]]; then
389                        COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
390                    else
391                        _filedir
392                    fi
393                    return 0
394                    ;;
395                attach|detach)
396                    case $cword in
397                        3)
398                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
399                            return 0
400                            ;;
401                        4)
402                            case $prev in
403                                id)
404                                    _bpftool_get_prog_ids
405                                    ;;
406                                name)
407                                    _bpftool_get_prog_names
408                                    ;;
409                                pinned)
410                                    _filedir
411                                    ;;
412                            esac
413                            return 0
414                            ;;
415                        5)
416                            local BPFTOOL_PROG_ATTACH_TYPES='sk_msg_verdict \
417                                sk_skb_verdict sk_skb_stream_verdict sk_skb_stream_parser \
418                                flow_dissector'
419                            COMPREPLY=( $( compgen -W "$BPFTOOL_PROG_ATTACH_TYPES" -- "$cur" ) )
420                            return 0
421                            ;;
422                        6)
423                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
424                            return 0
425                            ;;
426                        7)
427                            case $prev in
428                                id)
429                                    _bpftool_get_map_ids
430                                    ;;
431                                name)
432                                    _bpftool_get_map_names
433                                    ;;
434                                pinned)
435                                    _filedir
436                                    ;;
437                            esac
438                            return 0
439                            ;;
440                    esac
441                    ;;
442                load|loadall)
443                    local obj
444
445                    # Propose "load/loadall" to complete "bpftool prog load",
446                    # or bash tries to complete "load" as a filename below.
447                    if [[ ${#words[@]} -eq 3 ]]; then
448                        COMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) )
449                        return 0
450                    fi
451
452                    if [[ ${#words[@]} -lt 6 ]]; then
453                        _filedir
454                        return 0
455                    fi
456
457                    obj=${words[3]}
458
459                    if [[ ${words[-4]} == "map" ]]; then
460                        COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
461                        return 0
462                    fi
463                    if [[ ${words[-3]} == "map" ]]; then
464                        if [[ ${words[-2]} == "idx" ]]; then
465                            _bpftool_get_obj_map_idxs $obj
466                        elif [[ ${words[-2]} == "name" ]]; then
467                            _bpftool_get_obj_map_names $obj
468                        fi
469                        return 0
470                    fi
471                    if [[ ${words[-2]} == "map" ]]; then
472                        COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )
473                        return 0
474                    fi
475
476                    case $prev in
477                        type)
478                            local BPFTOOL_PROG_LOAD_TYPES='socket kprobe \
479                                kretprobe classifier flow_dissector \
480                                action tracepoint raw_tracepoint \
481                                xdp perf_event cgroup/skb cgroup/sock \
482                                cgroup/dev lwt_in lwt_out lwt_xmit \
483                                lwt_seg6local sockops sk_skb sk_msg \
484                                lirc_mode2 cgroup/bind4 cgroup/bind6 \
485                                cgroup/connect4 cgroup/connect6 \
486                                cgroup/getpeername4 cgroup/getpeername6 \
487                                cgroup/getsockname4 cgroup/getsockname6 \
488                                cgroup/sendmsg4 cgroup/sendmsg6 \
489                                cgroup/recvmsg4 cgroup/recvmsg6 \
490                                cgroup/post_bind4 cgroup/post_bind6 \
491                                cgroup/sysctl cgroup/getsockopt \
492                                cgroup/setsockopt cgroup/sock_release struct_ops \
493                                fentry fexit freplace sk_lookup'
494                            COMPREPLY=( $( compgen -W "$BPFTOOL_PROG_LOAD_TYPES" -- "$cur" ) )
495                            return 0
496                            ;;
497                        id)
498                            _bpftool_get_map_ids
499                            return 0
500                            ;;
501                        name)
502                            _bpftool_get_map_names
503                            return 0
504                            ;;
505                        pinned|pinmaps)
506                            _filedir
507                            return 0
508                            ;;
509                        *)
510                            COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
511                            _bpftool_once_attr 'type pinmaps autoattach'
512                            _bpftool_one_of_list 'offload_dev xdpmeta_dev'
513                            return 0
514                            ;;
515                    esac
516                    ;;
517                tracelog)
518                    return 0
519                    ;;
520                profile)
521                    case $cword in
522                        3)
523                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
524                            return 0
525                            ;;
526                        4)
527                            case $prev in
528                                id)
529                                    _bpftool_get_prog_ids
530                                    ;;
531                                name)
532                                    _bpftool_get_prog_names
533                                    ;;
534                                pinned)
535                                    _filedir
536                                    ;;
537                            esac
538                            return 0
539                            ;;
540                        5)
541                            COMPREPLY=( $( compgen -W "$METRIC_TYPE duration" -- "$cur" ) )
542                            return 0
543                            ;;
544                        6)
545                            case $prev in
546                                duration)
547                                    return 0
548                                    ;;
549                                *)
550                                    COMPREPLY=( $( compgen -W "$METRIC_TYPE" -- "$cur" ) )
551                                    return 0
552                                    ;;
553                            esac
554                            return 0
555                            ;;
556                        *)
557                            COMPREPLY=( $( compgen -W "$METRIC_TYPE" -- "$cur" ) )
558                            return 0
559                            ;;
560                    esac
561                    ;;
562                run)
563                    if [[ ${#words[@]} -eq 4 ]]; then
564                        COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
565                        return 0
566                    fi
567                    case $prev in
568                        id)
569                            _bpftool_get_prog_ids
570                            return 0
571                            ;;
572                        name)
573                            _bpftool_get_prog_names
574                            return 0
575                            ;;
576                        data_in|data_out|ctx_in|ctx_out)
577                            _filedir
578                            return 0
579                            ;;
580                        repeat|data_size_out|ctx_size_out)
581                            return 0
582                            ;;
583                        *)
584                            _bpftool_once_attr 'data_in data_out data_size_out \
585                                ctx_in ctx_out ctx_size_out repeat'
586                            return 0
587                            ;;
588                    esac
589                    ;;
590                *)
591                    [[ $prev == $object ]] && \
592                        COMPREPLY=( $( compgen -W 'dump help pin attach detach \
593                            load loadall show list tracelog run profile' -- "$cur" ) )
594                    ;;
595            esac
596            ;;
597        struct_ops)
598            local STRUCT_OPS_TYPE='id name'
599            case $command in
600                show|list|dump|unregister)
601                    case $prev in
602                        $command)
603                            COMPREPLY=( $( compgen -W "$STRUCT_OPS_TYPE" -- "$cur" ) )
604                            ;;
605                        id)
606                            _bpftool_get_map_ids_for_type struct_ops
607                            ;;
608                        name)
609                            _bpftool_get_map_names_for_type struct_ops
610                            ;;
611                    esac
612                    return 0
613                    ;;
614                register)
615                    _filedir
616                    return 0
617                    ;;
618                *)
619                    [[ $prev == $object ]] && \
620                        COMPREPLY=( $( compgen -W 'register unregister show list dump help' \
621                            -- "$cur" ) )
622                    ;;
623            esac
624            ;;
625        iter)
626            case $command in
627                pin)
628                    case $prev in
629                        $command)
630                            _filedir
631                            ;;
632                        id)
633                            _bpftool_get_map_ids
634                            ;;
635                        name)
636                            _bpftool_get_map_names
637                            ;;
638                        pinned)
639                            _filedir
640                            ;;
641                        *)
642                            _bpftool_one_of_list $MAP_TYPE
643                            ;;
644                    esac
645                    return 0
646                    ;;
647                *)
648                    [[ $prev == $object ]] && \
649                        COMPREPLY=( $( compgen -W 'pin help' \
650                            -- "$cur" ) )
651                    ;;
652            esac
653            ;;
654        map)
655            local MAP_TYPE='id pinned name'
656            case $command in
657                show|list|dump|peek|pop|dequeue|freeze)
658                    case $prev in
659                        $command)
660                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
661                            return 0
662                            ;;
663                        id)
664                            case "$command" in
665                                peek)
666                                    _bpftool_get_map_ids_for_type stack
667                                    _bpftool_get_map_ids_for_type queue
668                                    ;;
669                                pop)
670                                    _bpftool_get_map_ids_for_type stack
671                                    ;;
672                                dequeue)
673                                    _bpftool_get_map_ids_for_type queue
674                                    ;;
675                                *)
676                                    _bpftool_get_map_ids
677                                    ;;
678                            esac
679                            return 0
680                            ;;
681                        name)
682                            case "$command" in
683                                peek)
684                                    _bpftool_get_map_names_for_type stack
685                                    _bpftool_get_map_names_for_type queue
686                                    ;;
687                                pop)
688                                    _bpftool_get_map_names_for_type stack
689                                    ;;
690                                dequeue)
691                                    _bpftool_get_map_names_for_type queue
692                                    ;;
693                                *)
694                                    _bpftool_get_map_names
695                                    ;;
696                            esac
697                            return 0
698                            ;;
699                        *)
700                            return 0
701                            ;;
702                    esac
703                    ;;
704                create)
705                    case $prev in
706                        $command)
707                            _filedir
708                            return 0
709                            ;;
710                        type)
711                            local BPFTOOL_MAP_CREATE_TYPES="$(bpftool feature list_builtins map_types 2>/dev/null | \
712                                grep -v '^unspec$')"
713                            COMPREPLY=( $( compgen -W "$BPFTOOL_MAP_CREATE_TYPES" -- "$cur" ) )
714                            return 0
715                            ;;
716                        key|value|flags|entries)
717                            return 0
718                            ;;
719                        inner_map)
720                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
721                            return 0
722                            ;;
723                        id)
724                            _bpftool_get_map_ids
725                            ;;
726                        name)
727                            case $pprev in
728                                inner_map)
729                                    _bpftool_get_map_names
730                                    ;;
731                                *)
732                                    return 0
733                                    ;;
734                            esac
735                            ;;
736                        *)
737                            _bpftool_once_attr 'type key value entries name flags offload_dev'
738                            if _bpftool_search_list 'array_of_maps' 'hash_of_maps'; then
739                                _bpftool_once_attr 'inner_map'
740                            fi
741                            return 0
742                            ;;
743                    esac
744                    ;;
745                lookup|getnext|delete)
746                    case $prev in
747                        $command)
748                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
749                            return 0
750                            ;;
751                        id)
752                            _bpftool_get_map_ids
753                            return 0
754                            ;;
755                        name)
756                            _bpftool_get_map_names
757                            return 0
758                            ;;
759                        key)
760                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
761                            ;;
762                        *)
763                            case $(_bpftool_map_guess_map_type) in
764                                queue|stack)
765                                    return 0
766                                    ;;
767                            esac
768
769                            _bpftool_once_attr 'key'
770                            return 0
771                            ;;
772                    esac
773                    ;;
774                update|push|enqueue)
775                    case $prev in
776                        $command)
777                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
778                            return 0
779                            ;;
780                        id)
781                            _bpftool_map_update_get_id $command
782                            return 0
783                            ;;
784                        name)
785                            _bpftool_map_update_get_name $command
786                            return 0
787                            ;;
788                        key)
789                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
790                            ;;
791                        value)
792                            # We can have bytes, or references to a prog or a
793                            # map, depending on the type of the map to update.
794                            case "$(_bpftool_map_guess_map_type)" in
795                                array_of_maps|hash_of_maps)
796                                    local MAP_TYPE='id pinned name'
797                                    COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
798                                        -- "$cur" ) )
799                                    return 0
800                                    ;;
801                                prog_array)
802                                    local PROG_TYPE='id pinned tag name'
803                                    COMPREPLY+=( $( compgen -W "$PROG_TYPE" \
804                                        -- "$cur" ) )
805                                    return 0
806                                    ;;
807                                *)
808                                    COMPREPLY+=( $( compgen -W 'hex' \
809                                        -- "$cur" ) )
810                                    return 0
811                                    ;;
812                            esac
813                            return 0
814                            ;;
815                        *)
816                            case $(_bpftool_map_guess_map_type) in
817                                queue|stack)
818                                    _bpftool_once_attr 'value'
819                                    return 0;
820                                    ;;
821                            esac
822
823                            _bpftool_once_attr 'key'
824                            local UPDATE_FLAGS='any exist noexist'
825                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
826                                if [[ ${words[idx]} == 'value' ]]; then
827                                    # 'value' is present, but is not the last
828                                    # word i.e. we can now have UPDATE_FLAGS.
829                                    _bpftool_one_of_list "$UPDATE_FLAGS"
830                                    return 0
831                                fi
832                            done
833                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
834                                if [[ ${words[idx]} == 'key' ]]; then
835                                    # 'key' is present, but is not the last
836                                    # word i.e. we can now have 'value'.
837                                    _bpftool_once_attr 'value'
838                                    return 0
839                                fi
840                            done
841
842                            return 0
843                            ;;
844                    esac
845                    ;;
846                pin)
847                    case $prev in
848                        $command)
849                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
850                            ;;
851                        id)
852                            _bpftool_get_map_ids
853                            ;;
854                        name)
855                            _bpftool_get_map_names
856                            ;;
857                    esac
858                    return 0
859                    ;;
860                event_pipe)
861                    case $prev in
862                        $command)
863                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
864                            return 0
865                            ;;
866                        id)
867                            _bpftool_get_map_ids_for_type perf_event_array
868                            return 0
869                            ;;
870                        name)
871                            _bpftool_get_map_names_for_type perf_event_array
872                            return 0
873                            ;;
874                        cpu)
875                            return 0
876                            ;;
877                        index)
878                            return 0
879                            ;;
880                        *)
881                            _bpftool_once_attr 'cpu index'
882                            return 0
883                            ;;
884                    esac
885                    ;;
886                *)
887                    [[ $prev == $object ]] && \
888                        COMPREPLY=( $( compgen -W 'delete dump getnext help \
889                            lookup pin event_pipe show list update create \
890                            peek push enqueue pop dequeue freeze' -- \
891                            "$cur" ) )
892                    ;;
893            esac
894            ;;
895        btf)
896            local PROG_TYPE='id pinned tag name'
897            local MAP_TYPE='id pinned name'
898            case $command in
899                dump)
900                    case $prev in
901                        $command)
902                            COMPREPLY+=( $( compgen -W "id map prog file" -- \
903                                "$cur" ) )
904                            return 0
905                            ;;
906                        prog)
907                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
908                            return 0
909                            ;;
910                        map)
911                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
912                            return 0
913                            ;;
914                        id)
915                            case $pprev in
916                                prog)
917                                    _bpftool_get_prog_ids
918                                    ;;
919                                map)
920                                    _bpftool_get_map_ids
921                                    ;;
922                                $command)
923                                    _bpftool_get_btf_ids
924                                    ;;
925                            esac
926                            return 0
927                            ;;
928                        name)
929                            case $pprev in
930                                prog)
931                                    _bpftool_get_prog_names
932                                    ;;
933                                map)
934                                    _bpftool_get_map_names
935                                    ;;
936                            esac
937                            return 0
938                            ;;
939                        format)
940                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
941                            ;;
942                        *)
943                            # emit extra options
944                            case ${words[3]} in
945                                id|file)
946                                    _bpftool_once_attr 'format'
947                                    ;;
948                                map|prog)
949                                    if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
950                                        COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
951                                    fi
952                                    _bpftool_once_attr 'format'
953                                    ;;
954                                *)
955                                    ;;
956                            esac
957                            return 0
958                            ;;
959                    esac
960                    ;;
961                show|list)
962                    case $prev in
963                        $command)
964                            COMPREPLY+=( $( compgen -W "id" -- "$cur" ) )
965                            ;;
966                        id)
967                            _bpftool_get_btf_ids
968                            ;;
969                    esac
970                    return 0
971                    ;;
972                *)
973                    [[ $prev == $object ]] && \
974                        COMPREPLY=( $( compgen -W 'dump help show list' \
975                            -- "$cur" ) )
976                    ;;
977            esac
978            ;;
979        gen)
980            case $command in
981                object)
982                    _filedir
983                    return 0
984                    ;;
985                skeleton)
986                    case $prev in
987                        $command)
988                            _filedir
989                            return 0
990                            ;;
991                        *)
992                            _bpftool_once_attr 'name'
993                            return 0
994                            ;;
995                    esac
996                    ;;
997                subskeleton)
998                    case $prev in
999                        $command)
1000                            _filedir
1001                            return 0
1002                            ;;
1003                        *)
1004                            _bpftool_once_attr 'name'
1005                            return 0
1006                            ;;
1007                    esac
1008                    ;;
1009                min_core_btf)
1010                    _filedir
1011                    return 0
1012                    ;;
1013                *)
1014                    [[ $prev == $object ]] && \
1015                        COMPREPLY=( $( compgen -W 'object skeleton subskeleton help min_core_btf' -- "$cur" ) )
1016                    ;;
1017            esac
1018            ;;
1019        cgroup)
1020            case $command in
1021                show|list|tree)
1022                    case $cword in
1023                        3)
1024                            _filedir
1025                            ;;
1026                        4)
1027                            COMPREPLY=( $( compgen -W 'effective' -- "$cur" ) )
1028                            ;;
1029                    esac
1030                    return 0
1031                    ;;
1032                attach|detach)
1033                    local BPFTOOL_CGROUP_ATTACH_TYPES="$(bpftool feature list_builtins attach_types 2>/dev/null | \
1034                        grep '^cgroup_')"
1035                    local ATTACH_FLAGS='multi override'
1036                    local PROG_TYPE='id pinned tag name'
1037                    # Check for $prev = $command first
1038                    if [ $prev = $command ]; then
1039                        _filedir
1040                        return 0
1041                    # Then check for attach type. This is done outside of the
1042                    # "case $prev in" to avoid writing the whole list of attach
1043                    # types again as pattern to match (where we cannot reuse
1044                    # our variable).
1045                    elif [[ $BPFTOOL_CGROUP_ATTACH_TYPES =~ $prev ]]; then
1046                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
1047                                "$cur" ) )
1048                            return 0
1049                    fi
1050                    # case/esac for the other cases
1051                    case $prev in
1052                        id)
1053                            _bpftool_get_prog_ids
1054                            return 0
1055                            ;;
1056                        *)
1057                            if ! _bpftool_search_list "$BPFTOOL_CGROUP_ATTACH_TYPES"; then
1058                                COMPREPLY=( $( compgen -W \
1059                                    "$BPFTOOL_CGROUP_ATTACH_TYPES" -- "$cur" ) )
1060                            elif [[ "$command" == "attach" ]]; then
1061                                # We have an attach type on the command line,
1062                                # but it is not the previous word, or
1063                                # "id|pinned|tag|name" (we already checked for
1064                                # that). This should only leave the case when
1065                                # we need attach flags for "attach" commamnd.
1066                                _bpftool_one_of_list "$ATTACH_FLAGS"
1067                            fi
1068                            return 0
1069                            ;;
1070                    esac
1071                    ;;
1072                *)
1073                    [[ $prev == $object ]] && \
1074                        COMPREPLY=( $( compgen -W 'help attach detach \
1075                            show list tree' -- "$cur" ) )
1076                    ;;
1077            esac
1078            ;;
1079        perf)
1080            case $command in
1081                *)
1082                    [[ $prev == $object ]] && \
1083                        COMPREPLY=( $( compgen -W 'help \
1084                            show list' -- "$cur" ) )
1085                    ;;
1086            esac
1087            ;;
1088        net)
1089            local PROG_TYPE='id pinned tag name'
1090            local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload'
1091            case $command in
1092                show|list)
1093                    [[ $prev != "$command" ]] && return 0
1094                    COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1095                    return 0
1096                    ;;
1097                attach)
1098                    case $cword in
1099                        3)
1100                            COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
1101                            return 0
1102                            ;;
1103                        4)
1104                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
1105                            return 0
1106                            ;;
1107                        5)
1108                            case $prev in
1109                                id)
1110                                    _bpftool_get_prog_ids
1111                                    ;;
1112                                name)
1113                                    _bpftool_get_prog_names
1114                                    ;;
1115                                pinned)
1116                                    _filedir
1117                                    ;;
1118                            esac
1119                            return 0
1120                            ;;
1121                        6)
1122                            COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1123                            return 0
1124                            ;;
1125                        8)
1126                            _bpftool_once_attr 'overwrite'
1127                            return 0
1128                            ;;
1129                    esac
1130                    ;;
1131                detach)
1132                    case $cword in
1133                        3)
1134                            COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
1135                            return 0
1136                            ;;
1137                        4)
1138                            COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1139                            return 0
1140                            ;;
1141                    esac
1142                    ;;
1143                *)
1144                    [[ $prev == $object ]] && \
1145                        COMPREPLY=( $( compgen -W 'help \
1146                            show list attach detach' -- "$cur" ) )
1147                    ;;
1148            esac
1149            ;;
1150        feature)
1151            case $command in
1152                probe)
1153                    [[ $prev == "prefix" ]] && return 0
1154                    if _bpftool_search_list 'macros'; then
1155                        _bpftool_once_attr 'prefix'
1156                    else
1157                        COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )
1158                    fi
1159                    _bpftool_one_of_list 'kernel dev'
1160                    _bpftool_once_attr 'full unprivileged'
1161                    return 0
1162                    ;;
1163                list_builtins)
1164                    [[ $prev != "$command" ]] && return 0
1165                    COMPREPLY=( $( compgen -W 'prog_types map_types \
1166                        attach_types link_types helpers' -- "$cur" ) )
1167                    ;;
1168                *)
1169                    [[ $prev == $object ]] && \
1170                        COMPREPLY=( $( compgen -W 'help list_builtins probe' -- "$cur" ) )
1171                    ;;
1172            esac
1173            ;;
1174        link)
1175            case $command in
1176                show|list|pin|detach)
1177                    case $prev in
1178                        id)
1179                            _bpftool_get_link_ids
1180                            return 0
1181                            ;;
1182                    esac
1183                    ;;
1184            esac
1185
1186            local LINK_TYPE='id pinned'
1187            case $command in
1188                show|list)
1189                    [[ $prev != "$command" ]] && return 0
1190                    COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
1191                    return 0
1192                    ;;
1193                pin|detach)
1194                    if [[ $prev == "$command" ]]; then
1195                        COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
1196                    else
1197                        _filedir
1198                    fi
1199                    return 0
1200                    ;;
1201                *)
1202                    [[ $prev == $object ]] && \
1203                        COMPREPLY=( $( compgen -W 'help pin show list' -- "$cur" ) )
1204                    ;;
1205            esac
1206            ;;
1207    esac
1208} &&
1209complete -F _bpftool bpftool
1210
1211# ex: ts=4 sw=4 et filetype=sh
1212