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 --legacy'
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='msg_verdict \
411                                skb_verdict stream_verdict 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                            return 0
509                            ;;
510                    esac
511                    ;;
512                tracelog)
513                    return 0
514                    ;;
515                profile)
516                    case $cword in
517                        3)
518                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
519                            return 0
520                            ;;
521                        4)
522                            case $prev in
523                                id)
524                                    _bpftool_get_prog_ids
525                                    ;;
526                                name)
527                                    _bpftool_get_prog_names
528                                    ;;
529                                pinned)
530                                    _filedir
531                                    ;;
532                            esac
533                            return 0
534                            ;;
535                        5)
536                            COMPREPLY=( $( compgen -W "$METRIC_TYPE duration" -- "$cur" ) )
537                            return 0
538                            ;;
539                        6)
540                            case $prev in
541                                duration)
542                                    return 0
543                                    ;;
544                                *)
545                                    COMPREPLY=( $( compgen -W "$METRIC_TYPE" -- "$cur" ) )
546                                    return 0
547                                    ;;
548                            esac
549                            return 0
550                            ;;
551                        *)
552                            COMPREPLY=( $( compgen -W "$METRIC_TYPE" -- "$cur" ) )
553                            return 0
554                            ;;
555                    esac
556                    ;;
557                run)
558                    if [[ ${#words[@]} -eq 4 ]]; then
559                        COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
560                        return 0
561                    fi
562                    case $prev in
563                        id)
564                            _bpftool_get_prog_ids
565                            return 0
566                            ;;
567                        name)
568                            _bpftool_get_prog_names
569                            return 0
570                            ;;
571                        data_in|data_out|ctx_in|ctx_out)
572                            _filedir
573                            return 0
574                            ;;
575                        repeat|data_size_out|ctx_size_out)
576                            return 0
577                            ;;
578                        *)
579                            _bpftool_once_attr 'data_in data_out data_size_out \
580                                ctx_in ctx_out ctx_size_out repeat'
581                            return 0
582                            ;;
583                    esac
584                    ;;
585                *)
586                    [[ $prev == $object ]] && \
587                        COMPREPLY=( $( compgen -W 'dump help pin attach detach \
588                            load loadall show list tracelog run profile' -- "$cur" ) )
589                    ;;
590            esac
591            ;;
592        struct_ops)
593            local STRUCT_OPS_TYPE='id name'
594            case $command in
595                show|list|dump|unregister)
596                    case $prev in
597                        $command)
598                            COMPREPLY=( $( compgen -W "$STRUCT_OPS_TYPE" -- "$cur" ) )
599                            ;;
600                        id)
601                            _bpftool_get_map_ids_for_type struct_ops
602                            ;;
603                        name)
604                            _bpftool_get_map_names_for_type struct_ops
605                            ;;
606                    esac
607                    return 0
608                    ;;
609                register)
610                    _filedir
611                    return 0
612                    ;;
613                *)
614                    [[ $prev == $object ]] && \
615                        COMPREPLY=( $( compgen -W 'register unregister show list dump help' \
616                            -- "$cur" ) )
617                    ;;
618            esac
619            ;;
620        iter)
621            case $command in
622                pin)
623                    case $prev in
624                        $command)
625                            _filedir
626                            ;;
627                        id)
628                            _bpftool_get_map_ids
629                            ;;
630                        name)
631                            _bpftool_get_map_names
632                            ;;
633                        pinned)
634                            _filedir
635                            ;;
636                        *)
637                            _bpftool_one_of_list $MAP_TYPE
638                            ;;
639                    esac
640                    return 0
641                    ;;
642                *)
643                    [[ $prev == $object ]] && \
644                        COMPREPLY=( $( compgen -W 'pin help' \
645                            -- "$cur" ) )
646                    ;;
647            esac
648            ;;
649        map)
650            local MAP_TYPE='id pinned name'
651            case $command in
652                show|list|dump|peek|pop|dequeue|freeze)
653                    case $prev in
654                        $command)
655                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
656                            return 0
657                            ;;
658                        id)
659                            case "$command" in
660                                peek)
661                                    _bpftool_get_map_ids_for_type stack
662                                    _bpftool_get_map_ids_for_type queue
663                                    ;;
664                                pop)
665                                    _bpftool_get_map_ids_for_type stack
666                                    ;;
667                                dequeue)
668                                    _bpftool_get_map_ids_for_type queue
669                                    ;;
670                                *)
671                                    _bpftool_get_map_ids
672                                    ;;
673                            esac
674                            return 0
675                            ;;
676                        name)
677                            case "$command" in
678                                peek)
679                                    _bpftool_get_map_names_for_type stack
680                                    _bpftool_get_map_names_for_type queue
681                                    ;;
682                                pop)
683                                    _bpftool_get_map_names_for_type stack
684                                    ;;
685                                dequeue)
686                                    _bpftool_get_map_names_for_type queue
687                                    ;;
688                                *)
689                                    _bpftool_get_map_names
690                                    ;;
691                            esac
692                            return 0
693                            ;;
694                        *)
695                            return 0
696                            ;;
697                    esac
698                    ;;
699                create)
700                    case $prev in
701                        $command)
702                            _filedir
703                            return 0
704                            ;;
705                        type)
706                            local BPFTOOL_MAP_CREATE_TYPES='hash array \
707                                prog_array perf_event_array percpu_hash \
708                                percpu_array stack_trace cgroup_array lru_hash \
709                                lru_percpu_hash lpm_trie array_of_maps \
710                                hash_of_maps devmap devmap_hash sockmap cpumap \
711                                xskmap sockhash cgroup_storage reuseport_sockarray \
712                                percpu_cgroup_storage queue stack sk_storage \
713                                struct_ops ringbuf inode_storage task_storage \
714                                bloom_filter'
715                            COMPREPLY=( $( compgen -W "$BPFTOOL_MAP_CREATE_TYPES" -- "$cur" ) )
716                            return 0
717                            ;;
718                        key|value|flags|entries)
719                            return 0
720                            ;;
721                        inner_map)
722                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
723                            return 0
724                            ;;
725                        id)
726                            _bpftool_get_map_ids
727                            ;;
728                        name)
729                            case $pprev in
730                                inner_map)
731                                    _bpftool_get_map_names
732                                    ;;
733                                *)
734                                    return 0
735                                    ;;
736                            esac
737                            ;;
738                        *)
739                            _bpftool_once_attr 'type'
740                            _bpftool_once_attr 'key'
741                            _bpftool_once_attr 'value'
742                            _bpftool_once_attr 'entries'
743                            _bpftool_once_attr 'name'
744                            _bpftool_once_attr 'flags'
745                            if _bpftool_search_list 'array_of_maps' 'hash_of_maps'; then
746                                _bpftool_once_attr 'inner_map'
747                            fi
748                            _bpftool_once_attr 'dev'
749                            return 0
750                            ;;
751                    esac
752                    ;;
753                lookup|getnext|delete)
754                    case $prev in
755                        $command)
756                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
757                            return 0
758                            ;;
759                        id)
760                            _bpftool_get_map_ids
761                            return 0
762                            ;;
763                        name)
764                            _bpftool_get_map_names
765                            return 0
766                            ;;
767                        key)
768                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
769                            ;;
770                        *)
771                            case $(_bpftool_map_guess_map_type) in
772                                queue|stack)
773                                    return 0
774                                    ;;
775                            esac
776
777                            _bpftool_once_attr 'key'
778                            return 0
779                            ;;
780                    esac
781                    ;;
782                update|push|enqueue)
783                    case $prev in
784                        $command)
785                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
786                            return 0
787                            ;;
788                        id)
789                            _bpftool_map_update_get_id $command
790                            return 0
791                            ;;
792                        name)
793                            _bpftool_map_update_get_name $command
794                            return 0
795                            ;;
796                        key)
797                            COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) )
798                            ;;
799                        value)
800                            # We can have bytes, or references to a prog or a
801                            # map, depending on the type of the map to update.
802                            case "$(_bpftool_map_guess_map_type)" in
803                                array_of_maps|hash_of_maps)
804                                    local MAP_TYPE='id pinned name'
805                                    COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
806                                        -- "$cur" ) )
807                                    return 0
808                                    ;;
809                                prog_array)
810                                    local PROG_TYPE='id pinned tag name'
811                                    COMPREPLY+=( $( compgen -W "$PROG_TYPE" \
812                                        -- "$cur" ) )
813                                    return 0
814                                    ;;
815                                *)
816                                    COMPREPLY+=( $( compgen -W 'hex' \
817                                        -- "$cur" ) )
818                                    return 0
819                                    ;;
820                            esac
821                            return 0
822                            ;;
823                        *)
824                            case $(_bpftool_map_guess_map_type) in
825                                queue|stack)
826                                    _bpftool_once_attr 'value'
827                                    return 0;
828                                    ;;
829                            esac
830
831                            _bpftool_once_attr 'key'
832                            local UPDATE_FLAGS='any exist noexist'
833                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
834                                if [[ ${words[idx]} == 'value' ]]; then
835                                    # 'value' is present, but is not the last
836                                    # word i.e. we can now have UPDATE_FLAGS.
837                                    _bpftool_one_of_list "$UPDATE_FLAGS"
838                                    return 0
839                                fi
840                            done
841                            for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
842                                if [[ ${words[idx]} == 'key' ]]; then
843                                    # 'key' is present, but is not the last
844                                    # word i.e. we can now have 'value'.
845                                    _bpftool_once_attr 'value'
846                                    return 0
847                                fi
848                            done
849
850                            return 0
851                            ;;
852                    esac
853                    ;;
854                pin)
855                    case $prev in
856                        $command)
857                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
858                            ;;
859                        id)
860                            _bpftool_get_map_ids
861                            ;;
862                        name)
863                            _bpftool_get_map_names
864                            ;;
865                    esac
866                    return 0
867                    ;;
868                event_pipe)
869                    case $prev in
870                        $command)
871                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
872                            return 0
873                            ;;
874                        id)
875                            _bpftool_get_map_ids_for_type perf_event_array
876                            return 0
877                            ;;
878                        name)
879                            _bpftool_get_map_names_for_type perf_event_array
880                            return 0
881                            ;;
882                        cpu)
883                            return 0
884                            ;;
885                        index)
886                            return 0
887                            ;;
888                        *)
889                            _bpftool_once_attr 'cpu'
890                            _bpftool_once_attr 'index'
891                            return 0
892                            ;;
893                    esac
894                    ;;
895                *)
896                    [[ $prev == $object ]] && \
897                        COMPREPLY=( $( compgen -W 'delete dump getnext help \
898                            lookup pin event_pipe show list update create \
899                            peek push enqueue pop dequeue freeze' -- \
900                            "$cur" ) )
901                    ;;
902            esac
903            ;;
904        btf)
905            local PROG_TYPE='id pinned tag name'
906            local MAP_TYPE='id pinned name'
907            case $command in
908                dump)
909                    case $prev in
910                        $command)
911                            COMPREPLY+=( $( compgen -W "id map prog file" -- \
912                                "$cur" ) )
913                            return 0
914                            ;;
915                        prog)
916                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
917                            return 0
918                            ;;
919                        map)
920                            COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
921                            return 0
922                            ;;
923                        id)
924                            case $pprev in
925                                prog)
926                                    _bpftool_get_prog_ids
927                                    ;;
928                                map)
929                                    _bpftool_get_map_ids
930                                    ;;
931                                $command)
932                                    _bpftool_get_btf_ids
933                                    ;;
934                            esac
935                            return 0
936                            ;;
937                        name)
938                            case $pprev in
939                                prog)
940                                    _bpftool_get_prog_names
941                                    ;;
942                                map)
943                                    _bpftool_get_map_names
944                                    ;;
945                            esac
946                            return 0
947                            ;;
948                        format)
949                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
950                            ;;
951                        *)
952                            # emit extra options
953                            case ${words[3]} in
954                                id|file)
955                                    _bpftool_once_attr 'format'
956                                    ;;
957                                map|prog)
958                                    if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
959                                        COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
960                                    fi
961                                    _bpftool_once_attr 'format'
962                                    ;;
963                                *)
964                                    ;;
965                            esac
966                            return 0
967                            ;;
968                    esac
969                    ;;
970                show|list)
971                    case $prev in
972                        $command)
973                            COMPREPLY+=( $( compgen -W "id" -- "$cur" ) )
974                            ;;
975                        id)
976                            _bpftool_get_btf_ids
977                            ;;
978                    esac
979                    return 0
980                    ;;
981                *)
982                    [[ $prev == $object ]] && \
983                        COMPREPLY=( $( compgen -W 'dump help show list' \
984                            -- "$cur" ) )
985                    ;;
986            esac
987            ;;
988        gen)
989            case $command in
990                object)
991                    _filedir
992                    return 0
993                    ;;
994                skeleton)
995                    case $prev in
996                        $command)
997                            _filedir
998                            return 0
999                            ;;
1000                        *)
1001                            _bpftool_once_attr 'name'
1002                            return 0
1003                            ;;
1004                    esac
1005                    ;;
1006                subskeleton)
1007                    case $prev in
1008                        $command)
1009                            _filedir
1010                            return 0
1011                            ;;
1012                        *)
1013                            _bpftool_once_attr 'name'
1014                            return 0
1015                            ;;
1016                    esac
1017                    ;;
1018                min_core_btf)
1019                    _filedir
1020                    return 0
1021                    ;;
1022                *)
1023                    [[ $prev == $object ]] && \
1024                        COMPREPLY=( $( compgen -W 'object skeleton subskeleton help min_core_btf' -- "$cur" ) )
1025                    ;;
1026            esac
1027            ;;
1028        cgroup)
1029            case $command in
1030                show|list|tree)
1031                    case $cword in
1032                        3)
1033                            _filedir
1034                            ;;
1035                        4)
1036                            COMPREPLY=( $( compgen -W 'effective' -- "$cur" ) )
1037                            ;;
1038                    esac
1039                    return 0
1040                    ;;
1041                attach|detach)
1042                    local BPFTOOL_CGROUP_ATTACH_TYPES='ingress egress \
1043                        sock_create sock_ops device \
1044                        bind4 bind6 post_bind4 post_bind6 connect4 connect6 \
1045                        getpeername4 getpeername6 getsockname4 getsockname6 \
1046                        sendmsg4 sendmsg6 recvmsg4 recvmsg6 sysctl getsockopt \
1047                        setsockopt sock_release'
1048                    local ATTACH_FLAGS='multi override'
1049                    local PROG_TYPE='id pinned tag name'
1050                    # Check for $prev = $command first
1051                    if [ $prev = $command ]; then
1052                        _filedir
1053                        return 0
1054                    # Then check for attach type. This is done outside of the
1055                    # "case $prev in" to avoid writing the whole list of attach
1056                    # types again as pattern to match (where we cannot reuse
1057                    # our variable).
1058                    elif [[ $BPFTOOL_CGROUP_ATTACH_TYPES =~ $prev ]]; then
1059                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
1060                                "$cur" ) )
1061                            return 0
1062                    fi
1063                    # case/esac for the other cases
1064                    case $prev in
1065                        id)
1066                            _bpftool_get_prog_ids
1067                            return 0
1068                            ;;
1069                        *)
1070                            if ! _bpftool_search_list "$BPFTOOL_CGROUP_ATTACH_TYPES"; then
1071                                COMPREPLY=( $( compgen -W \
1072                                    "$BPFTOOL_CGROUP_ATTACH_TYPES" -- "$cur" ) )
1073                            elif [[ "$command" == "attach" ]]; then
1074                                # We have an attach type on the command line,
1075                                # but it is not the previous word, or
1076                                # "id|pinned|tag|name" (we already checked for
1077                                # that). This should only leave the case when
1078                                # we need attach flags for "attach" commamnd.
1079                                _bpftool_one_of_list "$ATTACH_FLAGS"
1080                            fi
1081                            return 0
1082                            ;;
1083                    esac
1084                    ;;
1085                *)
1086                    [[ $prev == $object ]] && \
1087                        COMPREPLY=( $( compgen -W 'help attach detach \
1088                            show list tree' -- "$cur" ) )
1089                    ;;
1090            esac
1091            ;;
1092        perf)
1093            case $command in
1094                *)
1095                    [[ $prev == $object ]] && \
1096                        COMPREPLY=( $( compgen -W 'help \
1097                            show list' -- "$cur" ) )
1098                    ;;
1099            esac
1100            ;;
1101        net)
1102            local PROG_TYPE='id pinned tag name'
1103            local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload'
1104            case $command in
1105                show|list)
1106                    [[ $prev != "$command" ]] && return 0
1107                    COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1108                    return 0
1109                    ;;
1110                attach)
1111                    case $cword in
1112                        3)
1113                            COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
1114                            return 0
1115                            ;;
1116                        4)
1117                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
1118                            return 0
1119                            ;;
1120                        5)
1121                            case $prev in
1122                                id)
1123                                    _bpftool_get_prog_ids
1124                                    ;;
1125                                name)
1126                                    _bpftool_get_prog_names
1127                                    ;;
1128                                pinned)
1129                                    _filedir
1130                                    ;;
1131                            esac
1132                            return 0
1133                            ;;
1134                        6)
1135                            COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1136                            return 0
1137                            ;;
1138                        8)
1139                            _bpftool_once_attr 'overwrite'
1140                            return 0
1141                            ;;
1142                    esac
1143                    ;;
1144                detach)
1145                    case $cword in
1146                        3)
1147                            COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
1148                            return 0
1149                            ;;
1150                        4)
1151                            COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
1152                            return 0
1153                            ;;
1154                    esac
1155                    ;;
1156                *)
1157                    [[ $prev == $object ]] && \
1158                        COMPREPLY=( $( compgen -W 'help \
1159                            show list attach detach' -- "$cur" ) )
1160                    ;;
1161            esac
1162            ;;
1163        feature)
1164            case $command in
1165                probe)
1166                    [[ $prev == "prefix" ]] && return 0
1167                    if _bpftool_search_list 'macros'; then
1168                        _bpftool_once_attr 'prefix'
1169                    else
1170                        COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )
1171                    fi
1172                    _bpftool_one_of_list 'kernel dev'
1173                    _bpftool_once_attr 'full unprivileged'
1174                    return 0
1175                    ;;
1176                *)
1177                    [[ $prev == $object ]] && \
1178                        COMPREPLY=( $( compgen -W 'help probe' -- "$cur" ) )
1179                    ;;
1180            esac
1181            ;;
1182        link)
1183            case $command in
1184                show|list|pin|detach)
1185                    case $prev in
1186                        id)
1187                            _bpftool_get_link_ids
1188                            return 0
1189                            ;;
1190                    esac
1191                    ;;
1192            esac
1193
1194            local LINK_TYPE='id pinned'
1195            case $command in
1196                show|list)
1197                    [[ $prev != "$command" ]] && return 0
1198                    COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
1199                    return 0
1200                    ;;
1201                pin|detach)
1202                    if [[ $prev == "$command" ]]; then
1203                        COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
1204                    else
1205                        _filedir
1206                    fi
1207                    return 0
1208                    ;;
1209                *)
1210                    [[ $prev == $object ]] && \
1211                        COMPREPLY=( $( compgen -W 'help pin show list' -- "$cur" ) )
1212                    ;;
1213            esac
1214            ;;
1215    esac
1216} &&
1217complete -F _bpftool bpftool
1218
1219# ex: ts=4 sw=4 et filetype=sh
1220