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